2009-05-14 2 views
6

В an answer было интересное заявление:. «Это почти всегда плохая идея использовать функцию fscanf(), как он может оставить свой указатель на файл в неизвестном месте в случае неудачи я предпочитаю использовать fgets(), чтобы получить каждую строку в и затем sscanf() это. "Когда/почему это плохая идея использовать функцию fscanf()?

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

ответ

13

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

1 
    2b 
    c 

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

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

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

+1

Пожалуйста, измените 'получает()' в 'fgets()'. 'gets()' никогда не должен использоваться никогда. – Wiz

+0

Must'a был опечаткой :) Спасибо, что поймал это. –

0

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

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

+1

В то время как переполнение буфера является одной из проблем с набором функций scanf(), они не связаны с проблемой, заданной здесь. -1 – Sparr

+1

«Не могли бы вы объяснить, почему лучше использовать fgets() и sscanf() для чтения некоторого файла». Я расширил его вопрос. Я отвергаю ваш избыточный «-1» – cyberconte

+1

. Я беру слово «expand on why», чтобы ваш ответ основывался на уже представленной предпосылке, что является проблемой указателя на файл. Если бы он хотел ДРУГИЕ причины, он бы не связался с происхождением вопроса или не процитировал соответствующую его часть. – Sparr

2

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

1

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

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

4

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

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

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

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

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

2

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

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

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

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

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

ОК, может быть, не нещадно, но какой-то порка, безусловно, в порядке; о)

+0

Утверждение «принимать указатели в качестве аргументов, но не ограничивать длину» вводит в заблуждение: для большинства типов размеры фиксированы ('% i','% d', '% lf'), поэтому ограничения длины не нужны. Исключением является чтение строк с '% s'.Но даже при этом ограничение может быть определено путем добавления числа между '%' и 's':'% 99s' для символьной строки, объявленной как 'char s [100]'. –