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

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

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

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

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

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

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

1
jsmith123 11 Апр 2019 в 09:28

2 ответа

Лучший ответ

Проблема здесь в том, что формат "%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) ...

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

5
Some programmer dude 11 Апр 2019 в 12:17
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]" спецификатора формата каждый scanf по крайней мере в 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».

1
chux - Reinstate Monica 11 Апр 2019 в 11:31