scanf что остается во входном потоке после failer - PullRequest
0 голосов
/ 17 января 2019

Я выучил C несколько лет назад, используя K & R 2nd edition ANSI C. Я просматривал свои заметки, а я изучал более современный C из 2 других книг.

Я заметил, что K & R никогда не использует scanf в книге, за исключением одного раздела, в котором они представляют его. Они в основном используют функцию getline, которую они пишут в книге, которую они изменяют позже в книге, как только вводят указатели. Там getline отличается от gcc getline, что вызывало у меня некоторые проблемы, пока я не изменил название getline на ggetline.

Просматривая мои заметки, я нашел эту цитату:

Это упрощение удобно и внешне привлекательно, и оно работает, насколько это возможно. Проблема в том, что scanf не работает в более сложных ситуациях. В разделе 7.1 мы сказали, что звонки putchar и printf могут чередоваться. То же самое не всегда верно of scanf: у вас могут возникнуть проблемы, если вы попытаетесь смешать вызовы сканировать с вызовами getchar или getline. Хуже того, получается, что Обработка ошибок scanf не подходит для многих целей. Это говорит вам было ли преобразование успешным или нет (точнее, оно говорит вам сколько конверсий удалось), но больше ничего не говорит вам чем это (если вы не спросите очень осторожно). Как atoi и atof, scanf перестает читать символы при обработке ввода% d или% f и находит нечисловой символ Предположим, вы предложили пользователю введите номер, и пользователь случайно наберет букву «х». зсапЕ может вернуть 0, указывая, что он не может преобразовать число, но необратимый текст («х») остается в потоке ввода, если вы не придумайте какой-нибудь другой способ удалить его.

По этим причинам (и нескольким другим, которые я не буду беспокоить упоминание) обычно рекомендуется не использовать scanf для неструктурированный ввод, такой как запросы пользователя. Гораздо лучше читать целые строки с чем-то вроде getline (как мы делали все вдоль), а затем обработать линию как-то. Если линия должна быть одним числом, вы можете использовать atoi или atof для его преобразования. Если строка имеет более сложную структуру, вы можете использовать sscanf (который мы будем встретимся через минуту) разобрать его. (Лучше использовать sscanf, чем scanf потому что, когда sscanf не удается, вы имеете полный контроль над тем, что вы делаете следующий. С другой стороны, когда scanf завершается неудачей, вы зависите от где во входном потоке он вас покинул.)

Сначала я подумал, что это цитата из K & R, но я не могу найти ее в книге. Затем я понял, что это из записок лекции, которые я получил в Интернете, для того, кто несколько лет назад преподавал курс с использованием книги K & R.

конспект лекций

Я знаю, что книге K & R уже 30 лет, поэтому она датирована несколькими способами.

Эта цитата очень старая, поэтому мне было интересно, есть ли у scanf такое поведение или оно изменилось?

Scanf по-прежнему оставляет вещи во входном потоке, когда он терпит неудачу? например выше:

Предположим, вы предложили пользователю ввести число, а пользователь случайно набирает букву «х». scanf может вернуть 0, указывая что он не может преобразовать число, но необратимый текст ( «х») остается во входном потоке.

верно ли следующее?

putchar и printf могут чередоваться. То же самое не всегда верно of scanf: у вас могут возникнуть проблемы, если вы попытаетесь смешать вызовы сканировать с вызовами getchar или getline.

Значительно ли изменился scanf после написания цитат выше? Или они все еще верны сегодня?

Причину, по которой я спрашиваю, в новых книгах, которые я читаю, никто не упоминает об этих проблемах.

1 Ответ

0 голосов
/ 17 января 2019

scanf() это зло - используйте fgets() и затем анализируйте.


Деталь не в том, что scanf() совсем плохо.

1) Спецификаторы формата часто используются слабым образом

char buf[100];
scanf("%s", buf); // bad - no width limit

2) Возвращаемое значение ошибочно не проверяется

scanf("%99[\n]", buf); // what if use entered `"\n"`?
puts(buf); 

3) Когда ввод не такой, как ожидалось, неясно, что остается в stdin.

if (scanf("%d %d %d", &i, &j, &k) != 3) {
  // OK, not what is in `stdin`?
}

у вас могут возникнуть проблемы, если вы попытаетесь смешать вызовы на scanf с вызовами getchar или getline.

Да. Многие scanf() вызовы оставляют завершающий '\n' в stdin, который затем читается как пустая строка на getline(), fgets(). scanf() - это , а не для чтения строк . getline() и fgets() гораздо лучше подходят для чтения строки .


Сильно ли изменился scanf после написания цитат выше?

Только так много изменений может произойти, не испортив базу кода. @ Джонатан Леффлер

scanf() остается проблематичным. scanf() не может принять аргумент (после формата), чтобы указать, сколько символов нужно принять в char * место назначения.

Некоторые системы добавили дополнительные параметры формата, чтобы помочь.

Фундаментальные проблемы таковы:

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


Безопасность

Слабость scanf() и склонность кодера к кодированию scanf() плохо стали золотым прииском для хакеров.

IMO, C не имеет надежного набора функций пользовательского ввода.

...