помещающей символ 'с' в “стандартный ввод”, который по умол-
чанию является терминалом. Вывод можно направить в некоторый
файл с помощью обозначения > : если PROG использует PUTCHAR,
то командная строка
PROG>OUTFILE
приведет к записи стандартного вывода в файл OUTFILE, а не
на терминал. На системе UNIX можно также использовать поточ-
ный механизм. Строка
PROG \! ANOTHERPROG
помещает стандартный вывод PROG в стандартный ввод ANOTHERPROG. И опять PROG не будет осведомлена об изменении направления.
Вывод, осуществляемый функцией PRINTF, также поступает в
стандартный вывод, и обращения к PUTCHAR и PRINTF могут пе-
ремежаться.
Поразительное количество программ читает только из одно-
го входного потока и пишет только в один выходной поток; для
таких программ ввод и вывод с помощью функций GETCHAR,
PUTCHAR и PRINTF может оказаться вполне адекватным и для на-
чала определенно достаточным. Это особенно справедливо тог-
· 154 -
да, когда имеется возможность указания файлов для ввода и
вывода и поточный механизм для связи вывода одной программы
с вводом другой. Рассмотрим, например, программу LOWER, ко-
торая преобразует прописные буквы из своего ввода в строч-
ные:
#INCLUDE <STDIO.H>
MAIN() /* CONVERT INPUT TO LOWER CASE */
\(
INT C;
WHILE ((C = GETCHAR()) != EOF)
PUTCHAR(ISUPPER© ? TOLOWER© : C);
\)
“Функции” ISUPPER и TOLOWER на самом деле являются макроса-
ми, определенными в STDIO.H . Макрос ISUPPER проверяет, яв-
ляется ли его аргумент буквой из верхнего регистра, и возв-
ращает ненулевое значение, если это так, и нуль в противном
случае. Макрос TOLOWER преобразует букву из верхнего регист-
ра в ту же букву нижнего регистра. Независимо от того, как
эти функции реализованы на конкретной машине, их внешнее по-
ведение совершенно одинаково, так что использующие их прог-
раммы избавлены от знания символьного набора.
Если требуется преобразовать несколько файлов, то можно
собрать эти файлы с помощью программы, подобной утилите CAT
системы UNIX,
CAT FILE1 FILE2 ... \! LOWER>OUTPUT
и избежать тем самым вопроса о том, как обратиться к этим
файлам из программы. (Программа CAT приводится позже в этой
главе).
Кроме того отметим, что в стандартной библиотеке вво-
да/вывода “функции” GETCHAR и PUTCHAR на самом деле могут
быть макросами. Это позволяет избежать накладных расходов на
обращение к функции для обработки каждого символа. В главе 8
мы продемонстрируем, как это делается.
7.3. Форматный вывод - функция PRINTF
Две функции: PRINTF для вывода и SCANF для ввода (следу-
ющий раздел) позволяют преобразовывать численные величины в
символьное представлEние и обратно. Они также позволяют ге-
нерировать и интерпретировать форматные строки. Мы уже всюду
в предыдущих главах неформально использовали функцию PRINTF;
здесь приводится более полное и точное описание. Функция
PRINTF(CONTROL, ARG1, ARG2, ...)
·
155 -
преобразует, определяет формат и печатает свои аргументы в
стандартный вывод под управлением строки CONTROL. Управляю-
щая строка содержит два типа объектов: обычные символы, ко-
торые просто копируются в выходной поток, и спецификации
преобразований, каждая из которых вызывает преобразование и
печать очередного аргумента PRINTF.
Каждая спецификация преобразования начинается с символа
% и заканчивается символом преобразования. Между % и симво-
лом преобразования могут находиться:
· знак минус, который указывает о выравнивании преобразован-
ного аргумента по левому краю его поля.
· Строка цифр, задающая минимальную ширину поля. Преобразо-
ванное число будет напечатано в поле по крайней мере этой
ширины, а если необходимо, то и в более широком. Если пре-
образованный аргумент имеет меньше символов, чем указанная
ширина поля, то он будет дополнен слева (или справа, если
было указано выравнивание по левому краю)заполняющими сим-
волами до этой ширины. Заполняющим символом обычно являет-
ся пробел, а если ширина поля указывается с лидирующим ну-
лем, то этим символом будет нуль (лидирующий нуль в данном
случае не означает восьмеричной ширины поля).
· Точка, которая отделяет ширину поля от следующей строки
цифр.
· Строка цифр (точность), которая указывает максимальное
число символов строки, которые должны быть напечатаны, или
число печатаемых справа от десятичной точки цифр для пере-
менных типа FLOAT или DOUBLE.
· Модификатор длины L, который указывает, что соответствую-
щий элемент данных имеет тип LONG, а не INT.
Ниже приводятся символы преобразования и их смысл:
D - аргумент преобразуется к десятичному виду.
O - Аргумент преобразуется в беззнаковую восьмеричную форму
(без лидирующего нуля).
X - Аргумент преобразуется в беззнаковую шестнадцатеричную
форму (без лидирующих 0X).
U - Аргумент преобразуется в беззнаковую десятичную форму.
C - Аргумент рассматривается как отдельный символ.
S - Аргумент является строкой: символы строки печатаются до
тех пор, пока не будет достигнут нулевой символ или не бу-
дет напечатано количество символов, указанное в специфика-
ции точности.
E - Аргумент, рассматриваемый как переменная типа FLOAT или
DOUBLE, преобразуется в десятичную форму в виде
[-]M.NNNNNNE[+-]XX, где длина строки из N определяется
указанной точностью. Точность по умолчанию равна 6.
F - Аргумент, рассматриваемый как переменная типа FLOAT или
DOUBLE, преобразуется в десятичную форму в виде
[-]MMM.NNNNN, где длина строки из N определяется указанной
точностью. Точность по умолчанию равна 6. отметим, что эта
точность не определяет количество печатаемых в формате F
значащих цифр.
· 156 -
G - Используется или формат %E или %F, какой короче; незна-
чащие нули не печатаются.
Если идущий за % символ не является символом преобразования,
то печатается сам этот символ; следовательно,символ % можно
напечатать, указав %%.
Большинство из форматных преобразований очевидно и было
проиллюстрировано в предыдущих главах. Единственным исключе-
нием является то, как точность взаимодействует со строками.
Следующая таблица демонстрирует влияние задания различных
спецификаций на печать “HELLO, WORLD” (12 символов). Мы по-
местили двоеточия вокруг каждого поля для того, чтобы вы
могли видеть его протяженность.
:%10S: :HELLO, WORLD:
:%10-S: :HELLO, WORLD:
:%20S: : HELLO, WORLD:
:%-20S: :HELLO, WORLD :
:%20.10S: : HELLO, WOR:
:%-20.10S: :HELLO, WOR :
:%.10S: :HELLO, WOR:
Предостережение: PRINTF использует свой первый аргумент
для определения числа последующих аргументов и их типов. Ес-
ли количество аргументов окажется недостаточным или они бу-
дут иметь несоответственные типы, то возникнет путаница и вы
получите бессмысленные результаты.
Упражнение 7-1.
Напишите программу, которая будет печатать разумным об-
разом произвольный ввод. Как минимум она должна печатать
неграфические символы в восьмеричном или шестнадцатеричном
виде (в соответствии с принятыми у вас обычаями) и склады-
вать длинные строки.
7.4. Форматный ввод - функция SCANF
Осуществляющая ввод функция SCANF является аналогом
PRINTF и позволяет проводить в обратном направлении многие
из тех же самых преобразований. Функция
SCANF(CONTROL, ARG1, ARG2, ...)
читает символы из стандартного ввода, интерпретирует их в
соответствии с форматом, указанном в аргументе CONTROL, и
помещает результаты в остальные аргументы. Управляющий аргу-
мент описывается ниже; другие аргументы, каждый из которых
должен быть указателем, определяют, куда следует поместить
соответствующим образом преобразованный ввод.
Управляющая строка обычно содержит спецификации преобра-
зования, которые используются для непосредственной интерпре-
тации входных последовательностей. Управляющая строка может
содержать:
· пробелы, табуляции или символы новой строки (“символы пус-
тых промежутков”), которые игнорируются.
·
157 -
· Обычные символы (не %), которые предполагаются совпадающи-
ми со следующими отличными от символов пустых промежутков
символами входного потока.
· Спецификации преобразования, состоящие из символа %, нео-
бязательного символа подавления присваивания *, необяза-
тельного числа, задающего максимальную ширину поля и сим-
вола преобразования.
Спецификация преобразования управляет преобразованием
следующего поля ввода. нормально результат помещается в пе-
ременную, которая указывается соответствующим аргументом.
Если, однако , с помощью символа * указано подавление прис-
ваивания, то это поле ввода просто пропускается и никакого
присваивания не производится. Поле ввода определяется как
строка символов, которые отличны от символов простых проме-
жутков; оно продолжается либо до следующего символа пустого
промежутка, либо пока не будет исчерпана ширина поля, если
она указана. Отсюда следует, что при поиске нужного ей вво-
да, функция SCANF будет пересекать границы строк, поскольку
символ новой строки входит в число пустых промежутков.
Символ преобразования определяет интерпретацию поля вво-
да; согласно требованиям основанной на вызове по значению
семантики языка “с” соответствующий аргумент должен быть
указателем. Допускаются следующие символы преобразования:
D - на вводе ожидается десятичное целое; соответствующий ар-
гумент должен быть указателем на целое.
O - На вводе ожидается восьмеричное целое (с лидирующим ну-
лем или без него); соответствующий аргумент должен быть
указателем на целое.
X - На вводе ожидается шестнадцатеричное целое (с лидирующи-
ми 0X или без них); соответствующий аргумент должен быть
указателем на целое.
H - На вводе ожидается целое типа SHORT; соответсвующий ар-
гумент должен быть указателем на целое типа SHORT.
C - Ожидается отдельный символ; соответствующий аргумент