Курс на Си. Подбельский. Курс программирования на Си. В., Фомин С. С. Курс программирования на языке Си Учебник
Скачать 1.57 Mb.
|
h - указывает, что следующий после h спецификатор d, i, o, x или X применяется к аргументу типа short или unsigned short; l - указывает, что следующий после l спецификатор d, i, o, x или X применяется к аргументу типа long или unsigned long; L - указывает, что следующий после L спецификатор e, E, f, g или G применяется к аргументу типа long double. Примеры указания ширины_поля и точности: %d - вывод десятичного целого в поле, достаточном для представления всех его цифр и знака; %7d - вывод десятичного целого в поле из 7 позиций; %f - вывод вещественного числа с целой и дробной частями (выравнивание по правому краю; количество цифр в дробной части определяется реализацией и обычно равно 6); %7f - вывод вещественного числа в поле из 7 позиций; %.3f - вывод вещественного числа с тремя цифрами после десятичной точки; %7.3f - вывод вещественного числа в поле из 7 позиций и тремя цифрами после десятичной точки; %.0f - вывод вещественного числа без десятичной точки и без дробной части; %15s - печать в выводимой строке не менее 15 символов; %. 12s - печать строки длиной не более 12 символов («лишние» символы не выводятся); %12.12 - печать всегда в поле из 12 позиций, причем лишние символы не выводятся, используются всегда 12 позиций. Таким образом, точное количество выводимых символов можно контролировать, задавая и ширину_поля, и точность (максимальное количество символов); %-15s - выравнивание выводимой строки по левому краю; %08f - вывод вещественного числа в поле из 8 позиций. Не занятые значащими цифрами позиции заполняются нулями. Функция форматного вывода printf( ) предоставляет множество возможностей по форматированию выводимых данных. Рассмотрим на примере построение столбцов данных и их выравнивание по левому или правому краям заданного поля. В программе, приводимой ниже, на экран дисплея выводится список товаров в магазине. В каждой строке в отдельных полях указываются: номер товара (int), код товара (int), наименование товара (строка символов) и цена товара (float). #include { int i; int number[3]={1, 2, 3}; /* Номер товара */ int code[3]={10, 25670, 120}; /* Код товара */ /* Наименование товара: */ char design[3][30]={{"лампа"}, {"стол"}, {"очень большое кресло"}}; /* Цена: */ float price[3]={1152.70, 2400.00, 4824.00}; for (i=0; i<=2; i++) printf("%-3d %5d %-20s %8.2f\n", number[i], code[i], design[i], price[i]); return 0; } Результат выполнения программы:
Форматная строка в параметрах функции printf( ) обеспечивает следующие преобразования выводимых значений: переменная number[i] типа int выводится в поле шириной 3 символа и прижимается к левому краю (%-3d); переменная code[i] типа int выводится в поле шириной 5 символов и прижимается (по умолчанию) к правому краю (%5d); строка из массива design[i] выводится в поле шириной 20 символов и прижимается к левому краю (%-20s). Если в данной спецификации преобразования будет указано меньшее, чем 20, количество позиций, то самая длинная строка будет все равно выведена, однако последний столбец не будет выровнен; переменная price[i] типа float выводится в поле шириной 8 символов, причем после десятичной точки выводятся 2 символа, и выводимое значение прижимается к правому краю. Между полями, определенными спецификациями преобразований, выводится столько пробелов, сколько их явно задано в форматной строке между спецификациями преобразования. Таким образом, добавляя пробелы между спецификациями преобразования, можно производить форматирование всей выводимой таблицы. Напомним, что любые символы, которые появляются в форматной строке и не входят в спецификации преобразования, копируются в выходной поток. Этим обстоятельством можно воспользоваться для вывода явных разделителей между столбцами таблицы. Например, для этой цели можно использовать символ '*' или '|'. В последнем случае форматная строка в функции printf( ) будет выглядеть так: «%-3d | %5d | %-20s | %8.2f\n», и результат работы программы будет таким: 1 | 10 | лампа | 1152.70 2 | 25670 | стол | 2400.00 3 | 120 | очень большое кресло | 4824.00 Форматный ввод из входного потока. Форматный ввод из входного потока осуществляется функцией scanf( ). Прототип функции scanf( ) имеет вид: int scanf(const char * format, . . . ); При обращении к функции scanf( ) возможны две формы задания первого параметра: int scanf ( форматная_строка, список_аргументов ); int scanf ( указатель_на_форматную_строку, список_аргументов); Функция scanf( ) читает последовательности кодов символов (байты) из входного потока и интерпретирует их в соответствии с форматной_строкой как целые числа, вещественные числа, одиночные символы, строки. В первом варианте вызова функции форматная строка размещается непосредственно в списке аргументов. Во втором варианте вызова предполагается, что первый аргумент - это указатель типа char *, адресующий собственно форматную строку. Форматная строка в этом случае должна быть определена в программе как обычная строковая константа или переменная. После преобразования во внутреннее представление данные записываются в области памяти, определенные аргументами, которые следуют за форматной строкой. Каждый аргумент должен быть указателем на переменную, в которую будет записано очередное значение данных и тип которой соответствует типу, указанному в спецификации преобразования из форматной строки. Если аргументов недостаточно для данной форматной строки, то результат зависит от реализации (от операционной системы и от системы программирования). Если аргументов больше, чем требуется в форматной строке, «лишние» аргументы игнорируются. Последовательность кодов символов, которую функция scanf( ) читает из входного потока, как правило, состоит из полей (строк), разделенных символами промежутка или обобщенными пробельными символами. Поля просматриваются и вводятся функцией scanf( ) посимвольно. Чтение поля прекращается, если встретился пробельный символ или в спецификации преобразования точно указано количество читаемых символов (см. ниже). Функция scanf( ) завершает работу, если исчерпана форматная строка. При успешном завершении scanf( ) возвращает количество прочитанных полей (точнее, количество объектов, получивших значения при вводе). Значение EOF возвращается при возникновении ситуации «конец файла»; значение -1 - при возникновении ошибки преобразования данных. Форматная строка ограничена двойными кавычками и может включать: пробельные символы, отслеживающие разделение входного потока на поля. Пробельный символ указывает на то, что из входного потока надо считывать, но не сохранять все последовательные пробельные символы вплоть до появления непробельного символа. Один пробельный символ в форматной строке соответствует любому количеству последовательных пробельных символов во входном потоке; обычные символы, отличные от пробельных и символа '%'. Обработка обычного символа из форматной строки сводится к чтению очередного символа из входного потока. Если прочитанный символ отличается от обрабатываемого символа форматной строки, функция завершается с ошибкой. Несовпавший символ и следующие за ним входные символы остаются непрочитанными; спецификации преобразования. Спецификация преобразования имеет следующую форму: % * ширина_поля модификатор спецификатор Все символы в спецификации преобразования являются необязательными, за исключением символа '%', с которого она начинается (он и является признаком спецификации преобразования), и спецификатора, позволяющего указать ожидаемый тип элемента во входном потоке (табл. 7.2). Необязательные элементы спецификации преобразования имеют следующий смысл: * - звездочка, следующая за символом процента, запрещает запись значения, прочитанного из входного потока по адресу, задаваемому аргументом. Последовательность кодов из входного потока прочитывается функцией scanf(), но не преобразуется и не записывается в переменную, определенную очередным аргументом. Ширина_поля - положительное десятичное целое, определяющее максимальное число символов, которое может быть прочитано из входного потока. Фактически может быть прочитано меньше символов, если встретится пробельный символ или символ, который не может быть преобразован по заданной спецификации. Модификатор - позволяет задать длину переменной, в которую предполагается поместить вводимое значение. Модификатор может принимать следующие значения: L - означает, что соответствующий спецификации преобразования аргумент должен указывать на объект типа long double; l - означает, что аргумент должен быть указателем на переменную типа long, unsigned long или double; h - означает, что аргумент должен быть указателем на тип short. Спецификация преобразования имеет следующую форму: % * ширина_поля модификатор спецификатор Все символы в спецификации преобразования являются необязательными, за исключением символа '%', с которого она начинается (он и является признаком спецификации преобразования), и спецификатора, позволяющего указать ожидаемый тип элемента во входном потоке (табл. 7.2). Необязательные элементы спецификации преобразования имеют следующий смысл: * - звездочка, следующая за символом процента, запрещает запись значения, прочитанного из входного потока по адресу, задаваемому аргументом. Последовательность кодов из входного потока прочитывается функцией scanf(), но не преобразуется и не записывается в переменную, определенную очередным аргументом. Ширина_поля - положительное десятичное целое, определяющее максимальное число символов, которое может быть прочитано из входного потока. Фактически может быть прочитано меньше символов, если встретится пробельный символ или символ, который не может быть преобразован по заданной спецификации. Модификатор - позволяет задать длину переменной, в которую предполагается поместить вводимое значение. Модификатор может принимать следующие значения: L - означает, что соответствующий спецификации преобразования аргумент должен указывать на объект типа long double; l - означает, что аргумент должен быть указателем на переменную типа long, unsigned long или double; h - означает, что аргумент должен быть указателем на тип short. Таблица 7.2. Спецификаторы форматной строки для функции форматного ввода
Контроль заключается в том, что во входном потоке должна присутствовать именно строка «code:» (без кавычек). Строка строка1 используется для комментирования вводимых данных, может иметь произвольную длину и пропускается при вводе. Отметим, что стро- ка1 и строка2 не должны содержать внутри себя пробелов. Текст программы приводится ниже: #include int main() { int i; int ret; /* Код возврата функции scanf() */ char c, s[80]; ret=scanf("code: %d %*s %c %s", &i, &c, s); printf("\n i=%d c=%c s=%s", i, c, s); printf("\n \t ret = %d\n", ret); return 0; } Рассмотрим форматную строку функции scanf( ): "code: %d %*s %c %s" Строка «code:» присутствует во входном потоке для контроля вводимых данных и поэтому указана в форматной строке. Спецификации преобразования задают следующие действия: %d - ввод десятичного целого; %*s - пропуск строки (строка1 в приведенной выше форме ввода); %с - ввод одиночного символа; %s - ввод строки. Приведем результаты работы программы для трех различных наборов входных данных. Последовательность символов исходных данных: code: 5 поле2 D asd Результат выполнения программы: i=5 c=D s=asd ret=3 Значением переменной ret является код возврата функции scanf( ). Число 3 говорит о том, что функция scanf( ) ввела данные без ошибки и было обработано 3 входных поля (строки «code:» и «поле2» пропускаются при вводе). Последовательность символов исходных данных: code: 5 D asd Результат выполнения программы: i=5 c=a s=sd ret=3 Обратите внимание на то, что во входных данных пропущена строка перед символом D, использующаяся как комментарий. В результате символ D из входного потока был (в соответствии с форматной строкой) пропущен, а из строки «asd» в соответствии с требованием спецификации преобразования %c был введен символ 'a' в переменную с. Остаток строки «asd» (sd) был введен в массив символов s. Код возврата (ret=3) функции scanf( ) говорит о том, что функция завершила свою работу без ошибок и обработала 3 поля. Последовательность символов исходных данных: cod: 5 поле2 D asd Результат выполнения программы: i=40 c= s= ) ret=0 Вместо предусмотренной в форматной строке последовательности символов в данных входного потока допущена ошибка (набрано слово cod: вместо code:). Функция scanf( ) завершается аварийно (код возврата равен 0). Функция printf( ) в качестве результата напечатала случайные значения переменных i, c и s. Необходимо иметь в виду, что функция scanf( ), обнаружив какую-либо ошибку при преобразовании данных входного потока, завершает свою работу, а необработанные данные остаются во входном потоке. Если функция scanf( ) применяется для организации диалога с пользователем, то, будучи активирована повторно, она дочитает из входного потока «остатки» предыдущей порции данных, а не начнет анализ новой порции, введенной пользователем. Предположим, что программа запрашивает у пользователя номер дома, в котором он живет. Программа обрабатывает код возврата функции scanf( ) в предположении, что во входных данных присутствует одно поле. #include { int number; printf("Beegume номер дома: "); while (scanf("%d", &number) != 1) printfC’OuudKa. Введите номер дома: "); printf("HoMep вашего дома %d\n", number); return 0; } При правильном вводе данных (введено целое десятичное число) результат будет следующий: Введите номер дома: 25 Номер вашего дома 25 Предположим, что пользователь ошибся и ввел, например, следующую последовательность символов «@%». (Эти символы будут введены, если нажаты клавиши '2' и '5', но на верхнем регистре, то есть одновременно была нажата клавиша Введите номер дома: @% Ошибка. Введите номер дома: Ошибка. Введите номер дома: Ошибочный символ @ прерывает работу функции scanf( ), но сам остается во входном потоке, и вновь делается попытка его преобразования при повторном вызове функции в теле цикла while. Однако эта и последующие попытки ввода оказываются безуспешными, и программа переходит к выполнению бесконечного цикла. Необходимо до предоставления пользователю новой попытки ввода номера дома убрать ненужные символы из входного потока. Это предусмотрено в следующем варианте той же программы: #include int main() { int number; printf("Beegume номер дома: "); while (scanf("%d", &number) != 1) { /* Ошибка. Очистить входной поток: */ while (getchar() != '\n') printf("Ошибка. Введите номер дома: "); } printf("Номер вашего дома %d\n", number); return 0; } Теперь при неправильном вводе данных результат будет таким: Введите номер дома: @% Ошибка. Введите номер дома: 25 Номер вашего дома 25 Работа с файлами на диске Так же как это делается при работе со стандартными потоками ввода-вывода stdin и stdout, можно осуществлять работу с файлами на диске. Для этой цели в библиотеку языка Си включены следующие функции: |