вола преобразования.
Спецификация преобразования управляет преобразованием следующего поля ввода.
нормально результат помещается в пе- ременную, которая указывается соответствующим
аргументом. Если, однако , с помощью символа * указано подавление прис- ваивания,
то это поле ввода просто пропускается и никакого присваивания не производится. Поле
ввода определяется как строка символов, которые отличны от символов простых проме-
жутков; оно продолжается либо до следующего символа пустого промежутка, либо пока
не будет исчерпана ширина поля, если она указана. Отсюда следует, что при поиске
нужного ей вво- да, функция SCANF будет пересекать границы строк, поскольку символ
новой строки входит в число пустых промежутков.
Символ преобразования определяет интерпретацию поля вво- да; согласно требованиям
основанной на вызове по значению семантики языка "с" соответствующий аргумент должен
быть указателем. Допускаются следующие символы преобразования: D - на вводе ожидается
десятичное целое; соответствующий ар-
гумент должен быть указателем на целое. O - На вводе ожидается восьмеричное целое
(с лидирующим ну-
лем или без него); соответствующий аргумент должен быть
указателем на целое. X - На вводе ожидается шестнадцатеричное целое (с лидирующи-
ми 0X или без них); соответствующий аргумент должен быть
указателем на целое. H - На вводе ожидается целое типа SHORT; соответсвующий
ар-
гумент должен быть указателем на целое типа SHORT. C - Ожидается отдельный символ;
соответствующий аргумент
должен быть указателем на символы; следующий вводимый
символ помещается в указанное место. Обычный пропуск сим-
волов пустых промежутков в этом случае подавляется; для
чтения следующего символа, который не является символом
пустого промежутка, пользуйтесь спецификацией преобразо-
вания %1S. S - Ожидается символьная строка; соответствующий аргумент
должен быть указателем символов, который указывает на
массив символов, который достаточно велик для принятия
строки и добавляемого в конце символа \0. F - Ожидается число с плавающей точкой;
соответствующий ар-
гумент должен быть указателем на переменную типа FLOAT. Е - символ преобразования
E является синонимом для F. Формат
ввода переменной типа FLOAT включает необязательный знак,
строку цифр, возможно содержащую десятичную точку и нео-
бязательное поле экспоненты, состоящее из буквы E, за ко-
торой следует целое, возможно имеющее знак.
Перед символами преобразования D, O и X может стоять L, которая означает , что
в списке аргументов должен находиться указатель на переменную типа LONG, а не типа
INT. Аналогич- но, буква L может стоять перед символами преобразования E или F,
говоря о том, что в списке аргументов должен нахо- диться указатель на переменную
типа DOUBLE, а не типа FLOAT.
Например, обращение INT I; FLOAT X; CHAR NAME[50]; SCANF("&D %F %S", &I, &X,
NAME);
со строкой на вводе
25 54.32E-1 THOMPSON
приводит к присваиванию I значения 25,X - значения 5.432 и NAME - строки "THOMPSON",
надлежащим образом законченной символом \ 0. эти три поля ввода можно разделить
столькими пробелами, табуляциями и символами новых строк, сколько вы пожелаете.
Обращение
INT I;
FLOAT X;
CHAR NAME[50];
SCANF("%2D %F %*D %2S", &I, &X, NAME);
с вводом
56789 0123 45A72
присвоит I значение 56, X - 789.0, пропустит 0123 и поместит в NAME строку "45".
при следующем обращении к любой процеду- ре ввода рассмотрение начнется с буквы
A. В этих двух приме- рах NAME является указателем и, следовательно, перед ним не
нужно помещать знак &.
В качестве другого примера перепишем теперь элементарный калькулятор из главы
4, используя для преобразования ввода функцию SCANF:
#INCLUDE <STDIO.H>
MAIN() /* RUDIMENTARY DESK CALCULATOR */
\(
DOUBLE SUM, V;
SUM =0;
WHILE (SCANF("%LF", &V) !=EOF)
PRINTF("\T%.2F\N", SUM += V);
\)
выполнение функции SCANF заканчивается либо тогда, когда она исчерпывает свою
управляющую строку, либо когда некоторый элемент ввода не совпадает с управляющей
спецификацией. В качестве своего значения она возвращает число правильно сов- падающих
и присвоенных элементов ввода. Это число может быть
использовано для определения количества найденных элементов ввода. при выходе
на конец файла возвращается EOF; подчерк- нем, что это значение отлично от 0, что
следующий вводимый символ не удовлетворяет первой спецификации в управляющей строке.
При следующем обращении к SCANF поиск возобновляется непосредственно за последним
введенным символом.
Заключительное предостережение: аргументы функции SCANF должны быть указателями.
Несомненно наиболее распространен- ная ошибка состоит в написании
SCANF("%D", N);
вместо
SCANF("%D", &N);
7.5. Форматное преобразование в памяти
От функции SCANF и PRINTF происходят функции SSCANF и SPRINTF, которые осуществляют
аналогичные преобразования, но оперируют со строкой, а не с файлом. Обращения к
этим функ- циям имеют вид:
SPRINTF(STRING, CONTROL, ARG1, ARG2, ...)
SSCANF(STRING, CONTROL, ARG1, ARG2, ...)
Как и раньше , функция SPRINTF преобразует свои аргументы ARG1, ARG2 и т.д. В
соответствии с форматом, указанным в CONTROL, но помещает результаты в STRING, а
не в стандартный вывод. KОнечно, строка STRING должна быть достаточно велика, чтобы
принять результат. Например, если NAME - это символь- ный массив, а N - целое, то
SPRINTF(NAME, "TEMP%D", N);
создает в NAME строку вида TEMPNNN, где NNN - значение N.
Функция SSCANF выполняет обратные преобразования - она просматривает строку STRING
в соответствии с форматом в ар- гументе CONTROL и помещает результирующие значения
в аргу- менты ARG1, ARG2 и т.д.эти аргументы должны быть указателя- ми. В результате
обращения
SSCANF(NAME, "TEMP%D", &N);
переменная N получает значение строки цифр, следующих за TEMP в NAME.
Упражнение 7-2
Перепишите настольный калькулятор из главы 4, используя для ввода и преобразования
чисел SCANF и/или SSCANF.
7.6. Доступ к файлам
Все до сих пор написанные программы читали из стандарт- ного ввода и писали в
стандартный вывод, относительно кото- рых мы предполагали, что они магическим образом
предоставле- ны программе местной операционной системой.
Следующим шагом в вопросе ввода-вывода является написа- ние программы, работающей
с файлом, который не связан зара- нее с программой. одной из программ, которая явно
демонстри- рует потребность в таких операциях, является CAT, которая объединяет
набор из нескольких именованных файлов в стандар- тный вывод. Программа CAT используется
для вывода файлов на терминал и в качестве универсального сборщика ввода для программ,
которые не имеют возможности обращаться к файлам по имени. Например, команда
CAT X.C.Y.C
печатает содержимое файлов X.C и Y.C в стандартный вывод.
Вопрос состоит в том, как организовать чтение из имено- ванных файлов, т.е.,
как связать внешние имена, которыми мыслит пользователь, с фактически читающими
данные операто- рами.
Эти правила просты. Прежде чем можно считывать из неко- торого файла или записывать
в него, этот файл должен быть открыт с помощью функции FOPEN из стандартной библиотеки.
функция FOPEN берет внешнее имя (подобное X.C или Y.C), про- водит некоторые обслуживающие
действия и переговоры с опера- ционной системой (детали которых не должны нас касаться)
и возвращает внутреннее имя, которое должно использоваться при последующих чтениях
из файла или записях в него.
Это внутреннее имя, называемое "указателем файла", фак- тически является указателем
структуры, которая содержит ин- формацию о файле, такую как место размещения буфера,
текущая позиция символа в буфере, происходит ли чтение из файла или запись в него
и тому подобное. Пользователи не обязаны знать эти детали, потому что среди определений
для стандартного ввода-вывода, получаемых из файла STDIO.H, содержится опре- деление
структуры с именем FILE. Единственное необходимое для указателя файла описание демонстрируется
примером:
FILE *FOPEN(), *FP;
Здесь говорится, что FP является указателем на FILE и FOPEN возвращает указатель
на FILE. Oбратите внимание, что FILE является именем типа, подобным INT, а не ярлыку
струк- туры; это реализовано как TYPEDEF. (Подробности того, как все это работает
на системе UNIX, приведены в главе 8).
Фактическое обращение к функции FOPEN в программе имеет вид:
FP=FOPEN(NAME,MODE);
Первым аргументом функции FOPEN является "имя" файла, кото- рое задается в виде
символьной строки. Второй аргумент MODE ("режим") также является символьной строкой,
которая указы- вает, как этот файл будет использоваться. Допустимыми режи- мами
являются: чтение ("R"), запись ("W") и добавление ("A").
Если вы откроете файл, который еще не сущетвует, для за-
писи или добавления, то такой файл будет создан (если это возможно). Открытие
существующего файла на запись приводит к отбрасыванию его старого содержимого. Попытка
чтения несу- ществующего файла является ощибкой. Ошибки могут быть обус-
ловлены и другими причинами (например, попыткой чтения из
файла, не имея на то разрешения). При наличии какой-либо
ошибки функция возвращает нулевое значение указателя NULL (которое для удобства
также определяется в файле STDIO.H).
Другой необходимой вещью является способ чтения или за- писи, если файл уже открыт.
Здесь имеется несколько возмож- ностей, из которых GETC и PUTC являются простейшими.функция
GETC возвращает следующий символ из файла; ей необходим ука- затель файла, чтобы
знать, из какого файла читать. Таким об-
разом,
C=GETC(FP)
помещает в "C" следующий символ из файла, указанного посред- ством FP, и EOF,
если достигнут конец файла.
Функция PUTC, являющаяся обращением к функции GETC,
PUTC(C,FP)
помещает символ "C" в файл FP и возвращает "C". Подобно фун- кциям GETCHAR и
PUTCHAR, GETC и PUTC могут быть макросами, а не функциями.
При запуске программы автоматически открываются три фай- ла, которые снабжены
определенными указателями файлов. Этими файлами являются стандартный ввод, стандартный
вывод и стан- дартный вывод ошибок; соответствующие указатели файлов назы- ваются
STDIN, STDOUT и STDERR. Обычно все эти указатели свя- заны с терминалом, но STDIN
и STDOUT могут быть перенаправ- лены на файлы или в поток (PIPE), как описывалось
в разделе 7.2.
Функции GETCHAR и PUTCHAR могут быть определены в терми- налах GETC, PUTC, STDIN
и STDOUT следующим образом: #DEFINE GETCHAR() GETC(STDIN) #DEFINE PUTCHAR(C) PUTC(C,
STDOUT) При работе с файлами для форматного ввода и вывода можно ис- пользовать
функции FSCANF и FPRINTF. Они идентичны функциям SCANF и PRINTF, за исключением
того, что первым аргументом является указатель файла, определяющий тот файл, который
бу- дет читаться или куда будет вестись запись; управляющая строка будет вторым
аргументом.
Покончив с предварительными замечаниями, мы теперь в состоянии написать программу
CAT для конкатенации файлов. Используемая здесь основная схема оказывается удобной
во многих программах: если имеются аргументы в командной стро- ке, то они обрабатываются
последовательно. Если такие аргу- менты отсутствуют, то обрабатывается стандартный
ввод. Это позволяет использовать программу как самостоятельно, так и как часть большей
задачи.
#INCLUDE <STDIO.H>
MAIN(ARGC, ARGV) /*CAT: CONCATENATE FILES*/
INT ARGC;
CHAR *ARGV[];
\(
FILE *FP, *FOPEN();
IF(ARGC==1) /*NO ARGS; COPY STANDARD INPUT*/
FILECOPY(STDIN);
ELSE
WHILE (--ARGC > 0)
IF ((FP=FOPEN(*++ARGV,"R"))==NULL) \(
PRINTF("CAT:CAN'T OPEN %\N",*ARGV);
BREAK;
\) ELSE \(
FILECOPY(FP);
FCLOSE(FP);
\)
\)
FILECOPY(FP) /*COPY FILE FP TO STANDARD OUTPUT*/
FILE *FP;
\(
INT C;
WHILE ((C=GETC(FP)) !=EOF)
PUTC(C, STDOUT);
\)
Указатели файлов STDIN и STDOUT заранее определены в библио- теке ввода-вывода
как стандартный ввод и стандартный вывод; они могут быть использованы в любом месте,
где можно исполь- зовать объект типа FILE*.они однако являются константами, а не
переменными, так что не пытайтесь им что-либо присваи- вать.
Функция FCLOSE является обратной по отношению к FOPEN; она разрывает связь между
указателем файла и внешним именем, установленную функцией FOPEN, и высвобождает
указатель файла для другого файла.большинство операционных систем имеют не- которые
ограничения на число одновременно открытых файлов, которыми может распоряжаться
программа. Поэтому, то как мы поступили в CAT, освободив не нужные нам более объекты,
яв- ляется хорошей идеей. Имеется и другая причина для примене- ния функции FCLOSE
к выходному файлу - она вызывает выдачу информации из буфера, в котором PUTC собирает
вывод. (При нормальном завершении работы программы функция FCLOSE вызы- вается автоматически
для каждого открытого файла).
7.7. Обработка ошибок - STDERR и EXIT
Обработка ошибок в CAT неидеальна. Неудобство заключает- ся в том, что если один
из файлов по некоторой причине ока- зывается недоступным, диагностическое сообщение
об этом пе- чатается в конце объединенного вывода. Это приемлемо, если вывод поступает
на терминал, но не годится, если вывод пос- тупает в некоторый файл или через поточный
(PIPELINE) меха- низм в другую программу.
Чтобы лучше обрабатывать такую ситуацию, к программе точно таким же образом,
как STDIN и STDOUT, присоединяется второй выходной файл, называемый STDERR. Если
это вообще возможно, вывод, записанный в файле STDERR, появляется на терминале пользователя,
даже если стандартный вывод направ- ляется в другое место.
Давайте переделаем программу CAT таким образом, чтобы сообщения об ошибках писались
в стандартный файл ошибок.
"INCLUDE <STDIO.H>
MAIN(ARGC,ARGV) /*CAT: CONCATENATE FILES*/