RuLibrary.com

ГЛАВНАЯ | ПОИСК | ТОП | КАРТА САЙТА      

 
 


 

Керниган Ричи >> Язык C (страница 22)


вола преобразования.

Спецификация преобразования управляет преобразованием следующего поля ввода. нормально результат помещается в пе- ременную, которая указывается соответствующим аргументом. Если, однако , с помощью символа * указано подавление прис- ваивания, то это поле ввода просто пропускается и никакого присваивания не производится. Поле ввода определяется как строка символов, которые отличны от символов простых проме- жутков; оно продолжается либо до следующего символа пустого промежутка, либо пока не будет исчерпана ширина поля, если она указана. Отсюда следует, что при поиске нужного ей вво- да, функция 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*/

Название книги: Язык C
Автор: Керниган Ричи
Просмотрено 45094 раз

123456789101112131415161718192021222324252627282930313233


 
Page generation 0.003 seconds