fscanf чтение данных с разделителями-пробелами до возврата

Я пытался прочитать данные из txt файла. Текстовый файл имеет несколько записей, каждая из которых занимает новую строку, и каждая запись имеет переменную длину шестнадцатеричных байтов данных, разделенных некоторыми символами (скажем, пробелом ' '). Пример txt-файла выглядит следующим образом:

e4 e2 e2 e1 ff\n
f2 a2 22 34\n
ff ee dd\n

Используя scanf(fp,"%2x",buffer+offset) в цикле, я пытался загрузить каждый байт в байтовый буфер до конца каждой строки, отмечая полную запись. Основная проблема заключается в обнаружении символа новой строки, так как scanf полностью его игнорирует и переходит на следующую строку. Мой исходный код был

do{
    counter=fscanf(datain,"%2x",buffer1+offset);
    fprintf(stdout,"%#2x ",buffer1[offset]);
    offset+=counter;
}while(!feof(datain));

person HarryQ    schedule 26.02.2014    source источник


Ответы (2)


Альтернативный и обычно более простой способ выполнить такую ​​работу — прочитать всю строку с fgets() или getline() и затем обработать символы в строке используя sscanf(). Во многом это похоже на текущую схему, но вам нужно иметь возможность перемещаться по строке, поэтому здесь часто бывает полезна спецификация преобразования %n:

while (fgets(line, sizeof(line), datain) != 0)
{
    int line_offset = 0;
    int new_offset;
    while (sscanf(line + line_offset, "%2x%n", &buffer1[offset], &new_offset) == 1)
    {
        printf("%#.2x ", buffer1[offset]);
        offset++;
        line_offset += new_offset;
    }
}

Конверсия %n не учитывается при возврате с sscanf().

Обратите внимание, что это позволяет избежать некоторых других проблем, замеченных в другом ответе. Он не пытается обрабатывать данные, когда не было строки для чтения, и не пытается обрабатывать данные, когда их не осталось.

Кроме того, одним из преимуществ чтения строки за раз является то, что отчет об ошибках часто проще/лучше, когда вы можете указать весь контекст (строку), где произошла ошибка, а не застрять на том, что осталось от строки после некоторого неопределенного количество успешных конверсий. При наличии ошибочного символа в шестом поле в строке можно достаточно просто показать предыдущие пять полей, а также где ошибка.

person Jonathan Leffler    schedule 26.02.2014

добавляя %c после каждого шестнадцатеричного спецификатора, я мог извлечь пробел и символ новой строки из потока. Проверив этого персонажа, я мог узнать, что достигнута новая линия.

do{
    counter=fscanf(datain,"%2x%c",buffer1+offset,&followsymbol);
    fprintf(stdout,"Counter:%i\n",counter);
    if (counter==2)
         {fprintf(stdout,"data:%5x\tfollowsymbol:%5x\n",buffer1[offset],followsymbol);
          offset+=1;
         }
    if(followsymbol==0x0a && counter==2)
          printf("a nl symbol has been detected\n");
}while(!feof(datain));

Вывод из терминала

Counter:2
data:ffffffe4   followsymbol:   20
Counter:2
data:ffffffe2   followsymbol:   20
Counter:2
data:ffffffe2   followsymbol:   20
Counter:2
data:ffffffe1   followsymbol:   20
Counter:2
data:ffffffff   followsymbol:    a
a nl symbol has been detected
Counter:2
data:fffffff2   followsymbol:   20
Counter:2
data:ffffffa2   followsymbol:   20
Counter:2
data:   22      followsymbol:   20
Counter:2
data:   34      followsymbol:    a
a nl symbol has been detected
Counter:2
data:ffffffff   followsymbol:   20
Counter:2
data:ffffffee   followsymbol:   20
Counter:2
data:ffffffdd   followsymbol:    a
a nl symbol has been detected
Counter:65535
person HarryQ    schedule 26.02.2014
comment
Почему вы жестко кодируете 0x0a, может быть, вместо этого лучше использовать \n? - person t0mm13b; 26.02.2014
comment
Нужно if(counter==2 && followsymbol==0x0a). В противном случае followsymbol может быть предыдущим значением, если counter имеет значение EOF или 1. - person chux - Reinstate Monica; 26.02.2014
comment
fscanf(...,"%2x%c",...,followsymbol); должно быть fscanf...,"%2x%c",...,&followsymbol); (отсутствует &) - person chux - Reinstate Monica; 26.02.2014
comment
@chux Извините за двусмысленность, изменено, как было предложено. - person HarryQ; 26.02.2014
comment
Будьте очень осторожны с кодом, который печатает «вновь полученные данные», когда на самом деле преобразование не удалось. В равной степени будьте очень осторожны с кодом, который использует feof() для обнаружения EOF; это почти всегда неправильно. Обратите внимание, в частности, что когда последнее число в файле будет успешно прочитано, feof() не будет говорить EOF. Есть целый вопрос о while (!feof(file)) всегда неправильно, который вы должны прочитать. Использование do { … } while (!feof(file)); не является улучшением. - person Jonathan Leffler; 26.02.2014
comment
Выходные данные не соответствуют коду/входному файлу. Либо 1) в последней строке файла нет \n, либо 2) опубликованный вывод не показывает, что происходит после обнаружения последней новой строки. feof(datain) не станет истинным до тех пор, пока after counter=fscanf() не вернет что-то отличное от 2. Попробуйте напечатать counter после каждого fscnaf(), чтобы увидеть. - person chux - Reinstate Monica; 26.02.2014
comment
Второстепенный: counter должен иметь тип int и печататься с "%d". - person chux - Reinstate Monica; 26.02.2014
comment
@chux Я думаю, что разница в выводе возникла из-за feof (datain). После последнего \n в следующей строке было что-то, хотя мы считали ее пустой; в результате feof(datain) не возвращал ненулевое значение, что приводило к значению счетчика 63335. Я изменил вывод результата, только если counter==2 (что указывает на значимое чтение). - person HarryQ; 26.02.2014
comment
feof(datain) не возвращает значение true сразу после прочтения последнего char. Вместо этого он возвращает true после попытки чтения после последнего char. Использование возвращаемого значения из fgets(), fgetc() или fscanf() является лучшим подходом, чем использование feof() для определения момента остановки. Используйте feof() после получения неудачного чтения, чтобы определить, был ли сбой EOF или ошибкой ввода-вывода. - person chux - Reinstate Monica; 26.02.2014