Работа с буферизированным вводом
Буферизированный ввод часто удобен для пользователя, т.к. он предоставляет возможность редактирования входных данных до отправки их в программу, но для программиста он может стать источником дополнительных забот, когда задействован символьный ввод. Как можно было заметить в ряде приводимых ранее примеров, проблема заключается в том, что буферизированный ввод требует нажатия клавиши для передачи введенных данных. Это действие пересылает также символ новой строки, который программа должна обработать. Давайте исследуем эту и другие проблемы на примере программы угадывания чисел. Вы выбираете число, а компьютер пытается его угадать. В программе применяется довольно скучный метод, но мы сосредоточимся на вводе-выводе, а не на алгоритме. В листинге 8.4 приведена начальная версия программы, которая требует дальнейшей доработки.
Листинг 8.4. Программа guess. с

Вот пример выполнения программы:
Выберите целое число в интервале от 1 до 100. Я попробую угадать его. Нажмите клавишу у, если моя догадка верна и клавишу n в противном случае.
Вашим числом является 1? п
Ладно, тогда это 2?
Ладно, тогда это 3? п
Ладно, тогда это 4?
Ладно, тогда это 5?
У
Я знал, что у меня получится!
Символьный ввод-вывод и проверка достоверности ввода 307
Вопреки ожиданиям алгоритма, реализованного в программе, мы выбрали небольшое число. Обратите внимание на то, что после ввода n программа делает два предположения. Дело в том, что программа читает ответ n как отрицание того, что было загадано число 1, и затем считывает символ новой строки как отрицание того факта, что было загадано число 2.
Одно из решений предусматривает использование цикла while для отбрасывания остатка введенной строки, включая символ новой строки. Дополнительное достоинство такого подхода состоит в том, что ответы вроде по или по way будут трактоваться просто как n. Версия в листинге 8.4 интерпретирует по как два ответа. Ниже показан пример цикла, в котором эта проблема устранена:
while (getchar() != 'у') /* получить ответ, сравнить су*/
{
printf("Ладно, тогда это %d?\n",++guess); while (getchar); != '\n')
continue; /* пропустить оставшуюся часть входной строки*/
}
В случае применения этого цикла получается следующий вывод:
Выберите целое число в интервале от 1 до 100. Я попробую угадать его.
Нажмите клавишу у, если моя догадка верна и клавишу n в противном случае.
Вашим числом является 1? п
Ладно, тогда это 2? по
Ладно, тогда это 3? по sir
Ладно, тогда это 4? forget it
Ладно, тогда это 5?
У
Я знал, что у меня получится!
Проблема с символом новой строки решена. Гем не менее, вряд ли можно посчитать нормальным тот факт, что f трактуется как n. Для устранения этого дефекта можно воспользоваться оператором if, чтобы отфильтровать другие ответы. Прежде всего, определите переменную типа char для хранения ответа:
char response;
Затем внесите изменения в цикл, чтобы он приобрел следующий вид:
while ((response = getchar()) != 'у') /* получить ответ */
{
if (response == 'n')
printf("Ладно, тогда это %d?\n",++guess); else
printf("Принимаются только варианты у или n.\"); while (getchar() != '\n’)
continue; /* пропустить оставшуюся часть входной строки*/
}
Теперь вывод выглядит так:
308 Глава 8
Выберите целое число в интервале от 1 до 100. Я попробую угадать его.
Нажмите клавишу у, если моя догадка верна и клавишу n в противном случае.
Вашим числом является 1?
n
Ладно, тогда это 2? no
Ладно, тогда это 3? no sir
Ладно, тогда это 4? forget it
Принимаются только варианты у или n. n
Ладно, тогда это 5?
y
/Я знал, что у меня получится!
При написании интерактивных программ вы должны стараться предвосхищать возможности нарушения инструкций пользователями. Программу необходимо проектировать так, чтобы она элегантно обрабатывала ошибки пользователей. Пользователей следует уведомить о допущенной ошибке и дать дополнительный шанс.
Разумеется, вы должны предоставить пользователю четкие инструкции, однако независимо от того, насколько они ясны, всегда найдутся те, кто интерпретирует их неправильно, а затем обвинит вас в составлении непонятных инструкций.
Смешивание числового и символьного ввода
Предположим, что программа требует символьного ввода с помощью getchar() и числового ввода посредством scanf(). Каждая из этих функций хорошо делает свою работу, но смешивать их нелегко. Причина в том, что функция getchar() читает каждый символ, включая пробелы, символы табуляции и новой строки, в то время как scanf() при чтении чисел пропускает пробелы, символы табуляции и новой строки.
Читать дальше