Как использовать строку формата fscanf () - PullRequest
1 голос
/ 11 апреля 2019

Я использую fscanf () для чтения ввода из файла (я знаю, что должен использовать fgets (), но мне это не разрешено), и я не могу понять, как правильно использовать строку формата.

Ввод в формате: M 03f8ab8,1

Мне нужны буква, адрес и номер для сохранения в переменной.Вот что я получил до сих пор:

while(fscanf(file, " %s %s, %d", operation, address, &size) != -1)

Как написано, он помещает букву в правильную переменную (операцию), но добавляет число в конец адреса, а затем назначает что-то неопределенное дляпеременная размера.

Она должна помещать каждую в свою собственную соответствующую переменную (и игнорировать запятую)

Как настроить fscanf (), чтобы получить это правильно?

Ответы [ 2 ]

5 голосов
/ 11 апреля 2019

Проблема здесь в том, что формат "%s" читает пробел строк с разделителями, и, поскольку в 03f8ab8,1 нет пробела, все они будут считаны как одна строка.

Вы можете решить эту проблему с помощью формата "%[", который позволяет выполнять очень простое сопоставление с образцом. Например, вы можете использовать его, чтобы сказать fscanf, что нужно читать все до (но не включая) запятую. Как

fscanf(file, "%s %[^,], %d", operation, address, &size)

Смотри, например, это scanf (и семейство) ссылка для более подробной информации.

Кроме того, вам не следует сравнивать результат fscanf с -1, вместо этого проверьте, что он проанализировал правильное количество последовательностей, сравнив возвращаемое значение с 3:

while (fscanf(file, "%s %[^,], %d", operation, address, &size) == 3) ...

Обратите внимание, что приведенный выше формат не будет накладывать никаких ограничений на строки, которые он будет читать. Это может привести к переполнению ваших строк. Если ваши строки имеют фиксированный размер (т.е. они являются массивами), тогда используйте максимальную ширину поля формата, чтобы ограничить число символов, которые fscanf будет читать и помещать в ваш массив.

Например (вообще ничего не зная о ваших реальных строках / массивах):

while (fscanf(file, "%1s %8[^,], %d", operation, address, &size) == 3) ...

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

1 голос
/ 11 апреля 2019
fscanf(input_fp, "%30[^ ,\n\t]%30[^ ,\n\t]%30[^ ,\n\t]", ...

не использует ни ',', ни '\ n' в текстовом файле. Последующие попытки fscanf () также дают сбой и возвращают значение 0, которое, не будучи EOF, вызывает бесконечный цикл.


fscanf() решение для fgets()/sscanf() лучше обрабатывает потенциальные ошибки ввода-вывода и синтаксического анализа:

main()
{
    FILE *input_fp;
    FILE *output_fp;
    char buf[100];
    while (fgets(buf, sizeof buf, input_fp) != NULL) 
    {
      char name[30];  // Insure this size is 1 more than the width in scanf format.
      char age_array[30];
      char occupation[30];
      #define VFMT " %29[^ ,\n\t]"
      int n;  // Use to check for trailing junk

      if (3 == sscanf(buf, VFMT "," VFMT "," VFMT " %n", 
          name, age_array, occupation, &n) && buf[n] == '\0') 
      {
        // Suspect OP really wants this width to be 1 more
        if (fprintf(output_fp, "%-30s%-30s%-30s\n", name, age_array, occupation) < 0)
          break;
      } else
        break;  // format error
    }
    fclose(input_fp);
    fclose(output_fp);
}

Вместо вызова ferror () проверьте возвращаемые значения fgets (), fprintf ().

Подозреваемые необъявленные буферы поля OP были [30] и настроены scanf () соответственно.

Подробная информация о if (3 == sscanf(buf, VFMT "," ...

Значение if (3 == sscanf(...) && buf[n] == '\0') { становится истинным, когда:

1) ровно 3 "%29[^ ,\n\t]" спецификатора формата каждого сканирования, по крайней мере, в 1 символе.

2) buf [n] - конец строки. n устанавливается через спецификатор "% n". Предыдущее '' в "% n" вызывает любые последующие пробелы после использования последнего "%29[^ ,\n\t]". scanf () видит «% n», который указывает ему установить текущее смещение от начала сканирования, которое будет присвоено int, на которое указывает & n.

"VFMT "," VFMT "," VFMT " %n" объединяется компилятором в

" %29[^ ,\n\t], %29[^ ,\n\t], %29[^ ,\n\t] %n".

Я считаю, что первое легче поддерживать, чем второе.

Первый пробел в " %29[^ ,\n\t]" указывает sscanf () сканировать (использовать и не сохранять) 0 или более пробелов ('', '\ t', '\ n' и т. Д.). Остальное указывает sscanf () использовать и сохранять любые символы от 1 до 29, кроме ',', '\ n', '\ t', а затем добавить '\ 0'.

...