Когда / почему это плохая идея использовать функцию fscanf ()?

В ответе было интересное заявление: «Практически всегда плохая идея использовать fscanf() как она может оставить указатель файла в неизвестном месте при fscanf() . Я предпочитаю использовать fgets() чтобы получить каждую строку и затем sscanf() что. ”

Не могли бы вы рассказать, когда / почему лучше использовать fgets() и sscanf() для чтения некоторого файла?

Представьте файл с тремя строками:

  1 2b c 

Используя fscanf() для чтения целых чисел, первая строка будет читать отлично, а на второй строке fscanf() оставит вас на «b», не уверен, что делать fscanf() . Вам понадобится какой-то механизм для перемещения по входу мусора, чтобы увидеть третью строку.

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

Я предпочитаю последний подход сам, хотя я бы не согласился с утверждением, что «почти всегда плохая идея использовать fscanf() » … fscanf() отлично подходит для большинства вещей.

Случай, когда это входит в игру, – это когда вы сопоставляете персональные литералы. Предположим, что у вас есть:

 int n = fscanf(fp, "%d,%d", &i1, &i2); 

Рассмотрим два возможных входа « 323,A424 » и « 323A424 ».

В обоих случаях fscanf() вернет 1, а следующий прочитанный символ будет 'A' . Невозможно определить, соответствует ли запятая или нет.

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

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

Есть две причины:

  • scanf() может оставить stdin в состоянии, которое трудно предсказать; это затрудняет восстановление ошибок, если не невозможно (это не проблема с fscanf() ); а также
  • Весь scanf() принимает указатели в качестве аргументов, но не ограничивает длину, поэтому они могут перехватывать буфер и изменять несвязанные переменные, которые происходят после буфера, вызывая, по-видимому, случайные ошибки повреждения памяти, которые очень трудно понять, найти и отладить , особенно для менее опытных программистов на С.

Программисты Novice C часто путаются о указателях и операторе «адрес-из», и часто опускают & там, где это необходимо, или добавляют его «для хорошей меры», где это не так. Это приводит к «случайным» segfaults, которые могут быть трудными для них. Это не ошибка scanf() , поэтому я оставляю это в своем списке, но это стоит иметь в виду.

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

Любой, кто рекомендует scanf() для начинающего программиста C, должен быть беспощадно уничтожен.

Хорошо, может быть, не безжалостно , но какая-то порка определенно в порядке; o)

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

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

В принципе, нет возможности сказать, что функция не выходит за пределы области памяти, которую вы выделили для нее.

Появился ряд замен, например fnscanf, который пытается исправить эти функции, указав максимальный предел для чтения для читателя, что позволяет ему не переполняться.