Таблица 4.7. Модификаторы преобразования функции scanf()

Как видите, применение спецификаторов преобразования может быть весьма разнообразным, причем в таблицах были показаны далеко не все средства. Эти средства связаны главным образом с облегчением чтения выбранных данных из жестко форматированных источников, таких как перфокарты или другие записи данных. Поскольку в книге функция scanf() используется в основном как удобный инструмент для интерактивной передачи данных программе, такие экзотические особенности здесь не обсуждаются.
Символьные строки и форматированный ввод-вывод 147
Обработка ввода функцией scanf()
Давайте более подробно рассмотрим, как функция scanf() считывает поток вводимых данных. Предположим, что вы применяете спецификатор %d, чтобы прочитать целое число. Функция scanf() начинает читать поток ввода по одному символу за раз. Она пропускает пробельные символы (символы пробела, табуляции и новой строки) до тех пор, пока не натолкнется на символ, отличный от пробельного. Поскольку функция scanf() пытается прочитать целое число, она ожидает обнаружить цифровой символ или, возможно, знак (+ или -). Встретив цифру или знак, она запоминает этот символ и считывает следующий. Если это цифра, она сохраняет ее и читает следующий символ. Функция scanf() продолжает чтение и сохранение символов, пока не столкнется с нецифровым символом. Тогда функция приходит к заключению, что она достигла конца очередного целого числа. Функция scanf() помещает этот нецифровой символ обратно в поток ввода. Это означает, что в следующий раз, когда программа приступит к чтению потока ввода, она начнет его с ранее отклоненного нецифрового символа. Наконец, функция scanf() вычисляет числовое значение, соответствующее считанным ею цифрам (и, возможно, знаку), и заносит это значение в указанную переменную.
Если вы используете ширину поля, функция scanf() прекращает чтение при достижении конца поля или на первом пробельном символе, в зависимости от того, что произойдет раньше.
А что случится, если первый отличный от пробельного символ представляет собой, скажем, символ А, а не цифру? В таком случае функция scanf() тут же останавливается и помещает символ А (или другой) обратно в поток ввода. Указанной переменной значение не присваивается, и в следующий раз, когда программа будет читать поток ввода, она снова начнет его с А. Если вы применяете в программе только спецификаторы %d, то функция scanf() никогда не продвинется дальше этого символа А. Кроме того, если вы используете scanf() с несколькими спецификаторами, то язык С требует, чтобы функция прекращала чтение потока ввода при первом же отказе.
Чтение потока ввода с применением других числовых спецификаторов происходит так же, как в случае спецификатора %d. Главное различие между ними заключается в том, что функция scanf() может распознавать больше символов в качестве части числа. Например, спецификатор %х требует, чтобы функция scanf() распознавала символы a-f и А-F как шестнадцатеричные цифры. Спецификаторы с плавающей запятой требуют, чтобы функция scanf() распознавала десятичные точки, экспоненциальную форму записи и новую р-нотацию.
Если вы используете спецификатор %s, то допускается любой символ, отличный от пробельного, поэтому функция scanf() пропускает пробельные символы до появления первого непробельного символа, после чего сохраняет все неиробельные символы вплоть до следующего появления пробельного символа. Это означает, что спецификатор %s заставляет функцию scanf() читать одиночное слово, т.е. строку, которая не содержит пробельных символов. В случае указания ширины поля scanf() прекращает чтение при достижении конца поля или на первом пробельном символе, в зависимости от того, что произойдет раньше. С помощью ширины поля нельзя заставить функцию scanf() читать более одного слова для одного спецификатора %s. И последний момент: когда функция scanf() помещает строку в назначенный массив, она добавляет завершающий символ ‘\0’ с тем, чтобы сделать содержимое массива строкой С.
Если вы задаете спецификатор %с, то все вводимые символы запоминаются в исходном виде. Если следующим вводимым символом является символ пробела или новой строки, то он и присваивается указанной переменной; пробельные символы не пропускаются.
148 глава 4
В действительности функция scanf() не относится к числу наиболее часто используемых функций ввода в С. Она рассматривается здесь по причине своей универсальности (т.к. умеет читать все многообразие типов данных). В языке С доступно несколько других функций ввода вроде getchar() и fgets(), которые лучше подходят для решения специфичных задач, например, чтения одиночных символов или чтения строк, содержащих пробелы. Некоторые из этих функций будут рассмотрены в главах 7, 11 и 13. А пока для ввода целого числа или десятичной дроби, символа или строки можете применять функцию scanf().
Читать дальше