Вперед Назад Содержание

7. Ввод-вывод на потоках

Эта глава описывает функции для создания потоков и выполнения ввода и вывода на них. Как обсждается в Главе 6 [Краткий обзор ввода-вывода], поток - довольно абстрактное понятие, представляющее канал связи с файлом, устройством, или процессом.

7.1 Потоки

По историческим причинам, С тип структуры данных, которая представляет поток называется FILE,а не "поток". Так как большинство библиотечных функций имеет дело с объектами типа FILE *, иногда термина указатель на файл также используется, чтобы обозначить "поток". Это ведет к беспорядку терминологии во многих книгах о C, это руководство, однако, использует термины "файл" и "поток" только в техническом смысле.

Тип FILE объявлен в заголовочном файле "stdio.h".

FILE (тип_данных) - это тип данных, используемый, чтобы представить объекты потока. Объект FILE содержит всю внутреннюю информацию о состоянии относительно соединения со связанным файлом, включая такие вещи как индикатор позиции файла и информация буферизации. Каждый поток также имеет индикаторы ошибки и состояния конца файла, которые могут быть проверены функциями feof и ferror; см. раздел 7.13 [EOF и ошибки].

Объекты FILE размещены и управляются внутренне в соответствии c библиотечными функциями ввода -вывода. Не пробуйте создавать ваши собственные объекты типа FILE; пусть библиотеки, делают это. Ваши программы должны иметь дело только с указателями на эти объекты (то есть FILE*).

7.2 Стандартные потоки

Когда основная функция вашей программы вызывается, уже существуют три предопределенных потока. Они представляют "стандартные" входные и выходные каналы, которые были установлены для процесса.

Эти потоки объявлены в заголовочном файле "stdio.h".

FILE * stdin (переменная)

стандартный входной поток, который является нормальным источником ввода для программы.

FILE * stdout (переменная)

поток стандартного вывода, который используется для нормального вывода программы.

FILE * stderr (переменная)

стандартный поток ошибки, который используется для сообщений об ошибках и диагностики, выданной программой.

В системе GNU, Вы можете определять то, какие файлы или процессы соответствуют этим потокам, использующим трубопровод и средства переадресации, обеспеченные оболочкой. (Использование примитивов оболочки описано в Главе 9 [Интерфейс файловой системы]) Большинство других операционных систем обеспечивает подобные механизмы, но подробности того, как их использовать могут изменяться.

В библиотеке GNU C, stdin, stdout, и stderr - обычные переменные, которые Вы можете устанавливать точно так же как любые другие. Например, чтобы переназначить стандартный вывод файла, нужно выполнить:

fclose (stdout); stdout = fopen ("standard-output-file","w");
Обратите внимание, что в других системах stdin, stdout, и stderr являются макрокомандами, которые Вы не можете назначать обычным способом. Но Вы можете использовать freopen, чтобы получить эффект закрытия того и повторного открытия этого. См. раздел 7.3 [Открытие потоков].

7.3 Открытие потоков

Открытие файла функцией fopen создает новый поток и устанавливает соединение между потоком и файлом, возможно включая создание нового файла.

Все описанное в этом разделе объявлено в заголовочном файле "stdio.h".

FILE * fopen (const char *filename , const char * opentype) (функция)
функция fopen открывает поток для ввода - вывода в файл, и возвращает указатель на поток.

оpentype аргумент - это строка, которая управляет открытием файла и определяет атрибуты возникающего в результате потока. Она должна начинаться с одной из следующих последовательностей символов:

"r" Открывает существующий файл для чтения.

"w" Открывает файл для записи. Если файл уже существует, его длина обнуляется. Иначе создается новый файл.

"а" Открывает файл для добавления; то есть записи в конец файла. Если файл уже существует начальное содержимое неизменяется, и вывод потока добавляется в конец файла. Иначе, создается новый пустой файл.

"r+" Открывает существующий файл, и для чтения и для заипси. Начальное содержимое файла неизменяется, и начальная позиция файла - в начале файла.

"w+" Открывает файл, и для чтения и для запмси. Если файл уже существует, он усекается, чтобы обнулить длину. Иначе, создается новый файл.

"а+" Открывает или создают файл, и для чтения и для добавления в конец. Если файл существует, начальное содержимое неизменяется. Иначе, создается новый файл. Начальная позиция файла для чтения - в начале файла, но вывод всегда добавляется к концу файла.

Вы видите, что "а+" запрашивает поток, который может делать и ввод и вывод. Стандарт ANSI говорит, что при использовании такого потока, Вы должны вызвать fflush (см. раздел 7.17 [Буферизация потока]) или позиционирующую файл функцию типа fseek (см. раздел 7.15 [Позиционирование файла]) при переключении чтения на запись или наоборот. Иначе, внутренние буфера не будут освобождены правильно. Библиотека GNU С не имеет этого ограничения; Вы можете делать произвольное чтение и запись на потоке в любом порядке.

Библиотека GNU С определяет один дополнительный символ для использования в opentype: символ "x" настаивает на создании нового файла, если имя файла уже существует, fopen выдаст ошибку. Это эквивалентно O_EXCL опции открыавющей функции (см. раздел 8.10 [Флаги состояния файла]).

Символ "b" в opentype имеет стандартное значение; он запрашивает двоичный поток, а не текстовый поток. Но это не имеет никакое значения в POSIX системах (включая систему GNU). Если и "+" и "b" определен, они могут применяться в любом порядке. См. раздел 7.14 [Двоичные потоки].

Любые другие символы в opentype просто игнорируются. Они могут быть значимы в других системах.

Ели происходит ошибка , fopen возвращает пустой указатель.

Вы можете иметь многократные потоки (или описатели файла) указывающие на тот же самый файл, открытый в то же самое время. Если Вы только вводите, это работает правильно, но Вы должны быть внимательны если какой-то поток выходной. См. раздел 8.5 [Предосторожности, связанные с потоком/описателем].

Это верно в равной степени, в зависимости от того, находятся ли потоки в одной программе или в отдельных программах (что может легко случиться). Может оказаться более безопасным использование средств закрытия файла, для того, чтобы избежать одновременного доступа. См. раздел 8.11 [Блокировки файла].

int FOPEN_MAX (макрос)
Значение этой макрокоманды - целочисленное постоянное выражение, которое представляет минимальное число потоков, что могут быть открыты одновременно. Значение этой константы - по крайней мере восемь, включая три стандартных потока stdin, stdout, и stderr.

FILE * freopen (const char *filename, const char * opentype, FILE *stream) (функция)
Эта функция - подобна комбинации fclose и fopen. Она сначала закрывает упоминаемый поток, игнорируя любые ошибки, которые обнаружены в процессе. (Т.к. ошибки игнорируются, Вы не должны использовать freopen на выходном потоке, если Вы фактически выполнили вывод, используя поток.) А затем открывает файл filename с режимом opentype как в fopen, и связывает его с тем же самым потоком.

Если, если операция терпит неудачу, возвращается пустой указатель; иначе, freopen возвращает поток.

freopen традиционно используется, чтобы соединить стандартный поток типа stdin с файлом вашего собственного выбора. Это полезно в программах, в которых использование стандартного потока для некоторых целей является жестко закодированным. В библиотеке GNU С, Вы можете просто закрывать стандартные потоки и открывать новые через fopen. Но другие системы испытывают недостаток этой способности, так что использование freopen более переносимо.

7.4 Закрытие потоков

Когда поток закрывается с помощью fclose, соединение между потоком и файлом отменяется. После того, как Вы закрыли поток, Вы не можете выполнять какие-нибудь дополнительные операции на нем.

int fclose (FILE *stream) (функция)
Эта функция заркыает поток и прерывает соединение с соответствующим файлом. Любой буферизированный вывод дописывается, и любой буферизированный ввод отбрасывается. Функция fclose возвратит значение 0, если файл будет закрыт успешно, и EOF, если будет обнаружена ошибка.

Важно проверить ошибки, когда Вы вызываете fclose, чтобы закрыть выходной поток, потому что в это время могут быть обнаружены реальные, каждодневные ошибки. Например, когда fclose допишет остающийся буферизированный вывод, это мжет вызвать ошибку, потому что диск полон. Даже если Вы знаете, что буфер пуст, ошибки могут происходить при закрытии файла, если Вы используете NFS.

Функция fclose объявлена в "stdio.h".

Если ваша программа завершается, или если Вы вызываете функцию выхода (см. 22.3.1 [Нормальное окончание]), все открытые потоки автоматически закрываются.

Если ваша программа завершается каким-нибудь другим способом, типа, вызыва функции аварийного прекращения работы (см. раздел 22.3.4 [Прерывание выполнения программы]) или фатального сигнала (см. Главу 21 [Обработка сигналов]), открытые потоки могут быть закрыты неправильно. Буферизированный вывод может быть не дописан. Для подробной информации относительно буферизации потоков, см. раздел 7.17 [Буферизация потока].

7.5 Простой вывод символами или строками

Этот раздел описывает функции для выполнения символьно- и строчноориентированного вывода.

Эти функции объявлены в заголовочном файле "stdio.h".

int fputc ( int C, FILE *stream) (функция)
Функция fputc преобразовывает символ C, чтобы напечатать char без знака, и запишет его в поток stream. EOF возвращается, если происходит ошибка; иначе возвращается символ C.

int putc ( int C, FILE *stream) (функция)
Это аналог fputc, за исключением того, что большинство систем выполняет ее как макрокоманду, делая это быстрее. Одно из следствий - то, что она может оценивать аргумент потока больше чем один раз. putc ­ обычно лучшая функция, для записи одиночного символа.

int putchar ( int c) (функция)
Функция putchar эквивалентна putc со stdout как значением аргумента потока.

int fputs (const char * s, FILE *stream) (функция)
Функция fputs запишет строку s в поток stream. Пустой символ завершения не пишется. Эта функция так же не добавляет символ перевода строки.

Эта функция возвращает EOF, если происходит оибк записи, а иначе неотрицательное значение.

Например:

fputs ("Are ", stdout); fputs ("you ", stdout); puts ("hungry?\n", stdout);
Выводит текст `Are you hungry?' сопровождаемый символом перевода строки.

int puts (const char * s) (функция)
Эта функция запишет строку s в поток stdout сопровождая ее символом перевода строки. Пустой символ завершения строки не запишет.

puts - наиболее удобная функция для печати простых сообщений.
Например:

puts ("Это - сообщение.");
int putw ( int w, FILE *stream) (функция)
Эта функция напишет w (int) в поток stream. Она предусматривает совместимость с SVID, но мы рекомендуем, чтобы Вы использовали fwrite взамен (см. раздел 7.12 [Блочный ввод-вывод]).

7.6 Символьный ввод

Этот раздел описывает функции для выполнения символьно- и строчноориентированного ввода. Эти функции объявлены в заголовочном файле "stdio.h".

int fgetc (FILE * stream) (функция)
Эта функция читает следующий символ как char без знака из потока stream и возвращает значение, преобразованное в int. Если происходит условие конца файла или ошибка чтения, возвращается EOF.

int getc (FILE * stream) (функция)
Это - аналог fgetc, за исключением того, что для нее допустимо (и типично) выполнение как макрокоманды, которая оценивает аргумент stream больше чем один раз. getc часто сильно оптимизирована, так что это ­ обычно лучшая функция, чтобы читать одиночный символ.

int getchar (void) (функция)
Функция getchar эквивалентна getc с stdin вместо аргумента stream.

Вот пример функции, которая вводит используя fgetc. Она работала бы, точно также используя getc взамен, или используя getchar () вместо fgetc (stdin).

int y_or_n_p (const char *question) { fputs (question, stdout); while (1) { int c, answer; /* Напишем пробел, чтобы отделить ответ от вопроса. */ fputc (" ", stdout); /* Читаем первый символ строки. Это должен быть символ ответа, но может и не быть. */ c = tolower (fgetc (stdin)); answer = c /* Отбрасываем остальную входную строку. */ while (c != '\n') c = fgetc (stdin); /* Примем ответ, если он был допустим. */ if (answer == 'y') return 1; if (answer == 'n') return 0; /* Ответ был недопустим: просим о допустимом ответе. */ fputs ("Please answer y or n:", stdout); } }
int getw (FILE * stream) (функция)
Эта функция читает word (то есть int) из stream. Она предусматривает совместимость с SVID. Мы рекомендуем, чтобы Вы использовали вместо этого fread (см. раздел 7.12 [Блочный ввод-вывод]) .

7.7 Строчно ориетированный ввод

Так как много программ интерпретируют ввод на основе строк, удобно иметь функции, чтобы читать строку из stream.

Стандартный C имеет функции, чтобы делать это, но они не очень безопасны: пустые символы и даже длинные строки могут сбивать их. Библиотека GNU обеспечивает нестандартную функцию getline, которая позволяет читать строки надежно.

Другое расширение GNU, getdelim, обобщает getline. Она читает разграниченную запись, определенную как все после следующего вхождения заданного символа-разделителя.

Все эти функции объявлены в "stdio.h".

size_t getline (char ** lineptr, size _t * n, FILE * stream) (функция)
Эта функция читает всю строку из stream, сохраняя текст (включая символ перевода строки и пустой символ завершения) в буфере и хранит буферный адрес в * lineptr.

Перед вызовом getline, Вы должны поместить в *lineptr адрес буфера *n байт длиной, размещенный malloc. Если этот буфер достаточно большой чтобы вместить строку, getline, сохранит строку в этом буфере. Иначе, getline делает больший буфер используя realloc, сохраняя новый буферный адрес обратно в *lineptr и увеличенный size обратно в *n. См. раздел 3.3 [Беспрепятственное резервирование].

Если Вы устанавливаете *lineptr как пустой указатель, и обнуляете *n, перед обращением, то getline, зарезервирует начальный буфер для Вас, вызывая malloc.

В любом случае, когда getline завершается, *lineptr - это char * который указывает на текст строки.

Когда getline успешно завершвется, она возвращает число прочитанных символов (включая символ перевода строки, но не, включая пустой символ завершения). Это значение дает возможность Вам отличить пустые символы, которые являются частью строки от пустого символа, вставленного как признак конца.

Эта функция - расширение GNU, но это - рекомендуемый способ читать строки из stream. Альтернативные стандартные функции ненадежны.

Если происходитошибка происходит или достигнут конец файла, getline возвращает -1.

size_t getdelim (char ** lineptr, size _t * n, int delimiter, FILE * stream) (функция)
Эта функция - подобна getline за исключением того, что символ, который сообщает, чтобы она прекратила читать - не обязательно символ перевода строки. Аргумент delimiter определяет символ - разделитель; getdelim будет читать, пока не увидит этот символ (или конец файла).

Текст сохраняется в lineptr, включая символ - разделитель и пустой символ завершения. Подобно getline, getdelim делает lineptr большим, если он не достаточно большой.

getline фактически реализована в терминах getdelim, как показано ниже:

size_t getline (char **lineptr, size_t *n, FILE *stream) { return getdelim (lineptr, n, '\n', stream); } сhar * fgets (char * s, int count, FILE * stream) (функция)
Функция fgets читает символы из потока stream включая символ перевода строки и сохраняет их в строке s, добавляя пустой символ, чтобы отметить конец строки. Вы должны обеспечить место для count символов в s, но читается count - 1 символов. Дополнительное символьное место используется, чтобы содержать пустой символ в конце строки.

Если, система уже в конце файла, когда Вы вызываете fgets, то содержимое массива s, неизменяется, и возвращается пустой указатель. Пустой указатель также возвращается, если происходит ошибка чтения, возвращаемое значение - указатель s.

Предупреждение: если входные данные имеют пустой символ, Вы не может использовать fgets. Так что не используйте fgets, если Вы не знаете, что данные не могут содержать пустой символ. Не используйте ее, чтобы читать файлы, отредактированные пользователем, потому что, если пользователь вставляет пустой символ, Вы должны обработать это правильно или напечатать сообщение об ошибках. Мы рекомендуем использовать getline вместо fgets.

сhar * gets (char * s) (функция)
Эта функция читает символы из потока stdin до следующего символа символа перевода строки, и сохраняет их в строке s. Символ перевода строки отбрасывается (обратите внимание, что это отличает ее от поведения fgets, которая копирует символ перевода строки в строку). Если она сталкивается с ошибкой чтения или концом файла, она возвращает пустой указатель; иначе она возвращает s.

Предупреждение: эта функция очень опасна, потому что она не обеспечивает никакой защиты против переполнения строки s. Библиотека GNU включает ее только для совместимости. Вы должны всегда использовать fgets или getline взамен. Чтобы напомнить Вам это, компоновщик (при использовании GNU ld) выдаст предупреждение всякий раз, когда Вы используете gets.

7.8 Обратное чтение

В программах синтаксического анализатора часто полезно исследовать следующий символ во входном потокек без того, чтобы удалить его из потока. Это называется "заглядывание вперед" при вводе, потому что ваша программа бросает взгляд на то что она затем будет читать.

При использовании потока ввода - вывода, Вы можете заглядывать вперед при вводе первым чтением и затем обратным чтением (так называемм выталкиванием обратно в поток). Обратное чтение делает символ доступным для следующего обращения к fgetc или другой входной функции на этом потоке.

Что такое способ обратного чтения

Это иллюстрированное объяснение обратного чтения. Предположим, что Вы имеете поток, читая файл, который содержит только шесть символов, символы "foobar". Предположите, что Вы пока прочитали три символа. Ситуация выглядит следующим образом:

f o o b а r ^
так что следующий входной символ будет "b".

Если вместо того, чтобы читать "b" Вы выполняете обратное чтение символа "o", Вы получаете примерно такую ситуацию:

f o o b а r | o­ ^
так, что следующие входные символы будут "o" и "b".

Если Вы обратно читаете "9" вместо "o", Вы получите это:

f o o b а r | 9­ ^
так, что следующие входные символы будут "9" и "b".

Использование ungetc для осуществления обратного чтения

Функция для чтения символа обратно называется ungetc, потому что она обращает действие getc.

int ungetc ( int C, FILE *sream) (функция)
Функция ungetc помещает символ C обратно во входной поток. Так что следующий ввод из потока будет читать C прежде, чем что-нибудь еще.

Если С - EOF, ungetc не делает ничего и только возвращает EOF. Это позволяет Вам вызывть ungetc с возвращаемым значением getc без проверки ошибки из getc.

Символ, который Вы помещаете обратно, не обязательно тот который фактически читался из потока. Т. е. читать какой-нибудь символ из потока перед его обратным чтением не обязательно! Но это - странный способ писать программу; обычно ungetc используется только, чтобы читить обратно символ, который только что читался из того же самого потока.

Библиотека GNU С поддерживает только один символ pushback другими словами, нельзя вызвать ungetc дважды без ввода между вызовами. Другие системы могут позволять Вам, помещать обратно много символов; тогда чтение из потока восстанавливает символы в обратном порядке, от того как они были помещены.

Выталкивание обратных символов не изменяет файл; и воздействует только на внутреннюю буферизацию потока. Если вызывается позиционирующая файл функция (типа fseek или rewind; см. раздел 7.15 [Позиционирование файла]), все отложенные помещаемые-обратно символы отбрасываются.

Обратное чтение символа в поток, который находится в конце файла, стирает индикатор конца файла для потока. После того, как Вы читаете тот символ, пробуя читать снова Вы столкнетесь с концом файла.

Вот пример, показывающий использование getc и ungetc, чтобы перескочить символы промежутков. Когда эта функция достигает символа не-промежутка, она читает обратно этот символ, и он будет замечен снова на следующей операции чтения из потока.

#include <stdio.h> #include <ctype.h> void skip_whitespace (FILE * stream) { int с; do /* Нет нужды проверять EOF, потому что это - не isspace, а ungetc игнорирует EOF. */ c = getc (stream); while (isspace (c)); ungetc (c, stream); }

7.9 Форматированный вывод

Функции, описанные в этом разделе (printf и др.) обеспечивают, удобный способ выполнять форматированный вывод. Вы вызываете printf со строкой формата или строкой шаблона, которая определяет, как форматировать значения остающихся аргументов.

Если ваша программа не фильтр, который специально выполняет строчно- или символьно- ориентированную обработку, использование printf, или одной из других зависимых функций, описанных в этом разделе - обычно самый простой и наиболее краткий способ выполнить вывод. Эти функции особенно полезны для печати сообщений ошибок, таблицы данных, и т.п..

Основы форматированного вывода

Функция printf может использоваться, чтобы печатать любое число аргументов. Аргумент строки шаблона, который Вы обеспечиваете в обращении, обеспечивает информацию не только относительно числа дополнительных аргументов, но также относительно их типов и какой стиль должен использоваться для печати.

Обычные символы в строке шаблона просто записываются в выходной поток как есть, в то время как спецификации преобразования, представленные символом `%' в шаблоне заставляют последующие аргументы форматироваться при записи в выходной поток. Например:

int pct = 37; char filename[] = "foo.txt"; printf ("Processing of `%s' is %d%% finished.\nPlease be patient.\n", filename, pct);
Производит вывод:

Processing of `foo.txt' is 37% finished. Please be patient.
Этот пример показывает использование "%d" преобразования, чтобы определить, что int аргумент должен быть напечатан в десятичной записи, "%s" преобразования, чтобы определить печать строкового аргумента, и "%%" преобразования, чтобы печатать непосредственно символ "%".

Имеются также преобразования для печати целочисленного аргумента как значения без знака в восьмеричной, десятичной, или шестнадцатеричной системе счисления ("%o", "%u", или "%x", соответственно); или как символьного значения ("%c").

Числа с плавающей запятой могут быть напечатаны в нормальной, с фиксированной запятой записи, используя "%f" преобразование или в экспоненциальном представлении чисел, используя "%e" преобразование. "%g" преобразование использует или "%e" или формат "%f", в зависимости от того что более подходит для заданного числа.

Вы можете управлять форматированием более точно, написав модификаторы между "%" и символом, который указывает какое преобразование применить. Они немного изменяют обычное поведение преобразования. Например, большинство спецификаций преобразования разрешает Вам определять минимальную ширину поля и флаг, указывающий, хотите ли Вы чтобы результат выравнивался по правому или по левому краю поля.

Специфические флаги и модификаторы, которые разрешаются и их интерпретация, изменяются в зависимости от преобразований. Они все описаны более подробно в следующих разделах. Не волнуйтесь, если это все кажется чрезмерно сложным; Вы можете почти всегда получать приемлемый вывод без использования какого-нибудь из модификаторов вообще. Модификаторы обычно используются, чтобы делать просмотр вывода в таблицах.

Синтаксис преобразования вывода

Этот раздел обеспечивает подробности относительно точного синтаксиса спецификаций преобразования, которые могут появляться в printf строке шаблона.

Символы в строке шаблона, которые - не часть спецификации преобразования, печатаются как есть в выходной поток. Последовательности мультисимволов (см. Главу 18 [Расширенные символы]) разрешены в строке шаблона.

Спецификации преобразования в строке шаблона имеют общую форму:

% флаги ширины [. точность] тип преобразования

Например, в спецификаторе преобразования "%-10.8ld", "-" является флагом, "10" определяет ширину поля, точность - "8", символ "l" является модификатором типа, и "d" определяет стиль преобразования. (Этот специфический спецификатор типа говорит, что печатается long int аргумент в десятичной записи, с минимумом 8 цифр, выровненных по левому краю в поле по крайней мере шириной 10 символов.)

Более подробно, спецификации преобразования вывода состоят из начального символа `%', сопровождаемого последовательностью:

Вы можете также определять точность "*". Это означает что следующий аргумент в списке параметров (до фактического значения, которое будет напечатано) используется как точность. Значение должно быть int и игнорируется, если оно отрицательное. Если Вы определяете "*" и для ширины и для точности, то аргумент ширны предшествует аргументу точности. Другие С библиотеки могут не распознавать такой синтаксис.

Таблица форматов вывода Эта таблица содержит различные форматы вывода:

'%d', '%i':

Вывод целого числа как десятичного числа со знаком. См. Раздел 7.9.4 [Целочисленные Форматы].

'%d' и '%i' являются синонимами для printf, но отличаются при использовании scanf для ввода (см. Раздел 7.11.3 [Таблица форматов ввода]).

'%o':

Печатает целое число как восьмеричное число без знака. См. Раздел 7.9.4 [Целочисленные форматы].

'%u':

Печатает целое число как десятичное число без знака. См. Раздел 7.9.4 [Целочисленные форматы].

'%Z':

Печатает целое число как десятичное число без знака, принимая как тип size_t. Детали см. в Разделе 7.9.4 [Целочисленные Форматы]. Этот формат является расширением GNU.

'%x', '%X':

Печатают целое число как шестнадцатеричное без знака. '%x' использует символы нижнего регистра а '%X' - верхнего регистра. См. Раздел 7.9.4 [Целочисленные форматы].

'%f':

Печатает число с плавающей запятой в нормальной записи (с фиксированной запятой). См. подробности в Разделе 7.9.5 [Форматы с плавающей запятой].

'%e', '%E':

Печатают число с плавающей запятой в экспоненциальном представлении чисел. '%e' использует символы нижнего регистра, а '%E' - верхнего регистра.

'%g', '%G':

Выводят число с плавающей запятой либо в нормальном, либо в экспоненциальном представлении. '%g' использует символы нижнего регистра, а '%G' - верхнего регистра.

'%c':

Печатает одиночный символ. См. Раздел 7.9.6 [Другие форматы Вывода].

'%s':

Печатает строку. См. Раздел 7.9.6 [Другие форматы Вывода].

'%p':

Выводит значение указателя. См. Раздел 7.9.6 [Другие форматы Вывода].

'%n':

Содержит число уже напечатанных символов. См. Раздел 7.9.6 [Другие Форматы Вывода]. Обратите внимание, что эта спецификация формата никогда не производит никакого вывода.

'%m':

Печатает строку, соответствующую значению errno. (Этот формат формат является расширением GNU.) См. Раздел 7.9.6 [Другие Форматы Вывода].

'%%':

Печатает символ `%'. См. Раздел 7.9.6 [Другие Форматы Вывода].

Если синтаксис спецификации формата вывода является недопустимым, то результат непредсказуем. Если не достаточно аргументов функции, чтобы обеспечить значения для всех спецификаций преобразования в строке шаблона, или если аргументы неправильных типов, результаты непредсказуемы. Если Вы обеспечиваете большее количество аргументов чем используется в спецификации формата, дополнительные аргументы, просто игнорируются; это иногда полезно.

Целочисленные Форматы

Этот раздел описывает опции для '%d', '%i', '%o', '%u', '%x', '%X', и '%Z' спецификаций преобразования. Эти преобразования печата­ ют целых числа в различных форматах. Форматы '%d' и '%i' печатают целочисленный аргумент как деся­ тичное число со знаком; в то время как '%o', '%u', и '%x' печатают аргумент как восьмеричное, десятичное, или шестнадцатеричное без знака соответственно. Формат '%X' - точно то же что и '%x' за исключением того, что она использует символы 'ABCDEF' в качестве цифр вместо 'abcdef'. '%Z' подобна '%u' но принимает аргумент типа size_t.

Имеют значение следующие флаги:

'-'

Выравнивание слева результата в поле вывода (вместо нор­ мального выравнивания справа).

'+'

Для знаковых форматов '%d' и '%i', печатает знак '+', если значение положительно.

' '

Для знаковых форматов '%d' и '%i', если результат не начи­ нается со знака '+' или знака '-', то ставит перед ним пробел. '*' Для формата '%o' ставит '0' первой цифрой, как будто, увеличивая точность. Не делает ничего полезного для форматов '%d', '%i', или '%u'. Использование этого флага производит вывод, который может анализироваться функциями strtoul (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел]) и scanf с форматом '%i' (см. Раздел 7.11.4 [Числовые форматы ввода]).

'0'

Дополняют поле нулями вместо пробелов. Нули помещаются пос­ ле какой-нибудь индикации относительно знака. Этот флаг игнорируется, если указан флаг '-' , или если указана точность.

Если указана точность, она определяет минимальное число цифр; в случае необходимости выводятся дополнительные нули вначале. Если Вы не указываете точность, печатается столько цифр числа, сколько требуется. Если Вы преобразовываете значение нуля с явной нулевой точностью, то никакие символы не выводятся вообще.

Без модификатора типа, соответствующий аргумент обрабатывается как int (для знаковых преобразований '%i' и '%d') или int без знака (для преобразований без знака '%o', '%u', '%x', и '%X'). Заметьте, что т. к. printf и ее производные функции, любой аргумент типа char и short автоматически приводится к типу int аргументами, заданными по умолчанию. Для аргументов других целочисленных типов, Вы можете использовать следующие модификаторы:

' H '

Определяет, что аргумент - short int или short unsigned int.

' l '

Определяет, что аргумент - long int или long unsigned int

' L '

Определяет, что аргумент - long long int. (Этот тип яв­ ляется расширением, обеспечиваемым компилятором GNU С. На системах, которые не поддерживают сверхдлинные целые числа, это - тоже что long int.)

Модификаторы для типа аргумента не применимы к '%Z', так как единственной целью '%Z' является указать тип данных size_t.

Вот пример использования строки шаблона:

'| %5d| %-5d| %+5d| %+-5d| %5d| %05d| %5.0d| %5.2d| %d|\n '
для печати числа, используя различные опции для преобразования '%d':
| 0 | 0 | + 0 | + 0 | 0 | 00000| | 00 | 0 | | 1 | 1 | + 1 | + 1 | 1 | 00001| 1 | 01 | 1 | | -1 | -1 | -1 | -1 | -1 | -0001| -1 | -01 | -1 | |100000|100000|+ 100000|100000|100000|100000|100000|100000|...
В частности обратите внимание на то, что получается в последнем случае, где аргумент слишком большой, чтобы поместиться в минимальной заданной ширине поля.

Вот еще несколько примеров, показывающих, как выводятся беззнаковые целые под различными опциями формата, используя строку шаблона:

'| %5u | %5o | %5x | %5X | %*5o | %*5x | %*5X | %*10.8x | \n'
| 0 | 0 | 0 | 0 | 0 | 0x0 | 0X0 |0x00000000| | 1 | 1 | 1 | 1 | 01 | 0x1 | 0X1 |0x00000001| |100000|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|

Преобразования с плавающей запятой

Этот раздел содержит спецификации форматов вывода чисел с плаваю­ щей запятой: '%f', '%e', '%E', '%g', и '%G'. Формат '%f' печатает ар­ гумент в формате с фиксированной запятой, выводя его на экран в виде [-] ddd.ddd, где число цифр после десятичной точки определяется точ­ ностью, которую Вы указали.

Формат '%e' печатает аргумент в экспоненциальном представлении, выводя его на экран в виде [-] d.ddde [+ | -] dd. Число цифр после десятичной точки также определяется точностью. Экспонента всегда содержит по крайней мере две цифры. На этот формат похож '%E', но экспонента отмечена символом ' E ' вместо ' e '.

Форматы '%g' и '%G' печатают аргумент в стиле '%e' и '%E' соответственно, если экспонента меньше чем -4 или больше либо равна точности; в противном случае они используют стиль формата '%f'. Конечные нули из дробной части результата удаляются, а символ десятичной точки появляется только, если он сопровождается цифрой.

Чтобы изменить поведение функции, могут применяться следующие флаги:

'-'

Выравнивание слева результата в поле вывода. Обычно результат выравнивается справа.

'+'

Всегда выводится знак 'плюс' или 'минус'.

' '

Если результат не начинается со знака 'плюс' или 'минус', ставит перед ним пробел.

'#'

Определяет, что результат должен всегда включать десятичную точку, даже если за ней не следует никаких цифр. Для форматов '%g' и '%G', конечные нули после десятичной точки удаляться не будут.

'0'

Дополняет поле нулями вместо пробелов; нули помещаются после знака.

Этот флаг игнорируется, если указан флаг '-'.

Точность определяет, сколько цифр следуют за символом десятичной точки для форматов '%f','%e', и '%E'. Точность, заданная по умолчанию для этих форматов - 6. Если она явно задана как 0, то символ десятичной точки подавляется. Для форматов '%g' и '%G', точность определяет сколько значащих цифр печатать. Значащие цифры это первая цифра перед десятичной точкой, и все цифры после нее. Если для '%g' или '%G' точность - 0 или не задана, то она обрабатывается как если бы была 1. Если напечатанное значение не может быть выражено точно заданным количеством цифр, то значение округляется до ближайшего подходящего значения.

Без модификатора типа, форматы с плавающей запятой используют аргумент двойного типа. (По умолчанию любой аргумент типа float автоматически преобразуется в double.) Поддерживаются следующие модификаторы типов:

'L' Определяет, что аргумент типа long double. Вот несколько примеров

как при выводе используются различные форматы чисел с плавающей запятой. Все числа были напечатаны, используя следующий шаблон строки:

'|%12.4f|%12.4e|%12.4g|\n' | 0.0000| 0.0000e+00| 0| | 1.0000| 1.0000e+00| 1| | -1.0000| -1.0000e+00| -1| | 100.0000| 1.0000e+02| 100| | 1000.0000| 1.0000e+03| 1000| | 10000.0000| 1.0000e+04| 1e+04| | 12345.0000| 1.2345e+04| 1.234e+04| | 100000.0000| 1.0000e+05| 1e+05| | 123456.0000| 1.2346e+05| 1.234e+05|
Обратите внимание как формат '%g' выводит конечные нули.

Другие Форматы Вывода

Этот раздел описывает различные форматы, используемые printf.

Формат '%c' печатает одиночный символ. Аргумент типа int сначала преобразовывается в unsigned char. Может использоваться флаг '-' для задания выравнивания слева в поле вывода, но точность или модификатор типа не могут быть определены. Например:

printf ('%c%c%c%c%c', 'h', 'e', 'l', 'l', 'o');
выводит
`hello'.

printf ('%3s%-6s', 'no', 'where');
выводит
`nowhere'.

Если Вы случайно передаете в качестве аргумента для формата '%s' пустой указатель преобразования, библиотека GNU выведет '(null)'. Мы думаем, что это более полезно чем сообщение об ошибке.

fprintf (stderr, 'can't open `%s': %m\n', filename);
является эквивалентным:
fprintf (stderr, 'can't open `%s': %s\n', filename, strerror (errno));
Формат '%m' - расширение библиотеки GNU С.

Формат '%p' печатает значение указателя. Соответствующий аргумент должен иметь тип void*. Практически, Вы можете использовать любой тип указателя.

В системе GNU, непустые указатели печатаются как integers unsigned, как при использовании формата '%#x'. Пустые указатели печатаются как '(nil)'. (В других системах указатели могут печататься по-другому.)

Например:

printf ('%p', 'testing');
печатает '0x' сопровождаемый шестнадцатеричным числом адреса строковой константы 'testing'.

Вы можете добавить флаг '-' к формату '%p', чтобы обеспечить выравнивание слева, но не должны определяться никакие другие флаги, точность, или модификаторы типа.

Формат '%n' - отличается от всех других форматов вывода. Он использует аргумент, который должен быть указателем на int, но вместо того, чтобы печатать что-нибудь, он содержит число уже напечатанных символов. Модификаторы типа 'h' и 'l' задают, что указывается аргумент типа short int* или long int * вместо int *, но не позволяется указывать никакие флаги, ширину поля, или точность.

Например,

int nchar; printf ('%d %s%n\n', 3, 'bears', &nchar);
печатает:
3 bears
и устанавливает nchar в значение 7, потому что строка '3 bears' содержит семь символов.

Формат '%%' выводит символ `%'. Этот формат не использует аргумент.

Функции Форматированного Вывода

Этот раздел описывает, как вызвать printf и относящиеся к ней функции. Прототипы для этих функций находятся в файле 'stdio.h'. Т. к. эти функции принимают переменное число аргументов, Вы должны объявить прототипы для них перед использованием. Конечно, самый простой способ удостовериться, что Ваши прототипы все правильные, это включить 'stdio.h'.

- Функция: int printf (const char *TEMPLATE, ...)

Функция printf выводит указываемые аргументы под управлением шаблона строки TEMPLATE в поток stdout. Она возвращает число напечатанных символов, или отрицательное значение при ошибке вывода.

- Функция: int fprintf (FILE *stream, const char *template, ...)

Эта функция - аналог printf, за исключением того, что вывод записывается в указанный поток вместо stdout.

- Функция: int sprintf (char *s, const char *template, ...)

Она подобна printf, за исключением того, что вывод сохраняется в символьном массиве s вместо записи в поток. Пустой символ записывается в конец строки.

Функция sprintf возвращает число символов, содержащихся в массиве s, исключая пустой символ завершения.

Поведение этой функции неопределено, если копирование происходит между пересекающимися объектами. Например, если s также является аргументом, который выводится под управлением формата '%s'. См. Раздел 5.4 [Копирование и Конкатенация].

Предупреждение: функция sprintf может быть опасна, т. к. она может потенциально выводить большее количество символов чем размер, зарезервированный для строки s. Не забудьте, что ширина поля, заданная в спецификации формата - только минимальное значение.

Чтобы избежать этой проблемы, Вы можете использовать snprintf или asprintf, описанные ниже.

- Функция: int snprintf (char *s, size_t size, const char *TEMPLATE, ...)

Функция snprintf подобна sprintf, за исключением того, что аргумент размера определяет максимальное число выводимых символов. Конечный пустой символ подпадает под это ограничение

Возвращаемое значение - число сохраненных символов, не включая пустой символ завершения. Если это значение равняется size-1, то в s недостаточно места для всего вывода. Вы должны пробовать снова с большей строкой вывода. Вот пример такого кода:

/* Выдаем сообщение, описывающее значение переменной с именем NAME и значением VALUE */ char * make_message (char *name, char *value) { /* Предположим, что нам понадобится не больше чем 100 символов. */ int size = 100; char *buffer = (char *) xmalloc (size); while (1) { /* Попытаемся напечатать в отведенном пространстве. */ int nchars = snprintf (buffer, size, 'value of %s is %s', name, value); /* If that worked, return the string. */ if (nchars < size) return buffer; /* Иначе попытаемся еще раз с удвоенным количеством символов */ size *= 2; buffer = (char *) xrealloc (size, buffer); } }

На деле, часто проще использовать asprintf, см. ниже.

Форматируемый Вывод, Размещаемый Динамически

Функции в этом разделе форматируют вывод и помещают результаты в динамически размещенную память.

- Функция : int asprintf (char **ptr, const char *template, ...)

Эта функция похожа на sprintf, за исключением того, что она динамически распределяет строку для вывода (как malloc; см. Раздел 3.3 [Беспрепятственное Резервирование], вместо того, чтобы помещать вывод в буфер, определяемый заранее. Аргумент ptr должен быть адресом объекта char*, и asprintf сохраняет в нем указатель на размещенную строку.

char * make_message (char *name, char *value) { char *result; asprintf (&result, 'value of %s is %s', name, value); return result; }
-Функция: int obstack_printf(struct obstack* obstack,const char*

template,...)

Эта функция подобна asprintf, за исключением того, что она использует obstack, чтобы зарезервировать пространство в памяти. См. Раздел 3.4 [Obstack].

Символы дописываются в конец текущего объекта. Чтобы добраться до них, Вы должны закончить объект функцией obstack_finish (см. Раздел 3.4.6 [Возрастастающие Объекты]).

Переменные Аргументы Функций Вывода

Функции vprintf и подобные позволяют Вам определять ваши собственные различные printf-подобные функции, которые используют ту же самую внутреннюю организацию как и встроенные форматирующие функции вывода.

Наиболее естественный способ определять такие функции состоит в том, чтобы использовать конструкцию типа 'Вызвать printf и передать ему этот шаблон плюс все мои аргументы пропуская первые пять.' Но не имеется никакого способа сделать это на C, и было бы трудно это сделать, потому что на уровне Языка C не имеется никакого способа указать сколько аргументов получает ваша функция.

Так как тот метод невозможен, мы поставляем дополнительные функции типа vprintf, которые позволяет Вам передавать va_list, чтобы описать 'все мои аргументы после первых пяти.'

Перед вызовом vprintf или других функций, перечисленных в этом разделе, Вы должны вызвать va_start (см. Раздел A. 2 [Variadic Функции]) чтобы установить указатель на переменные аргументы. После этого Вы можете вызывать va_arg, чтобы выбрать аргументы, которые Вы хотели обработать.

Если ваш vуказатель a_list указывает на аргументы вашего выбора, Вы можете вызвать vprintf. Этот аргумент и все последующие аргументы, которые были переданы вашей функции, используются vprintf наряду с шаблоном, который Вы определили отдельно.

В некоторых других системах, указатель va_list может стать недопустимым после обращения к vprintf, так что Вы не должны использовать va_arg после того, как Вы вызываете vprintf. Вместо этого, Вы должны вызвать va_end, чтобы отменить указатель. Затем Вы можете безопасно вызывать va_start для другой переменной указателя и начинать выбирать аргументы снова через этот указатель. Вызов vprintf не разрушает список параметров вашей функции.

GNU C не имеет таких ограничений. Вы можете продолжать выбирать аргументы из списка va_list после выполнения через vprintf, тогда va_end - пустая команда. (Примечание, последующие обращения va_arg выберут те же самые аргументы, как и те, что предварительно использованы vprintf.)

Прототипы для этих функций объявлены в 'stdio.h'.

- Функция: int vprintf (const char *template, va_list ap)

Эта функция подобна printf за исключением того, что ей, вместо переменной, содержащей число аргументов, необходимо передавать указатель на список параметров.

- Функция: int vfprintf (FILE *stream, const char *template, va_list ap)

Эта функция - эквивалент fprintf с переменным списком параметров, заданным непосредственно как и для vprintf.

- Функция: int vsprintf (char *s, const char *template, va_list ap)

Эта функция - эквивалент sprintf с переменным списком параметров, заданным непосредственно как в vprintf.

- Функция: int vsnprintf (char *s, size_t size, const char *template, va_list ap)

Это функция - эквивалент snprintf с переменным списком параметров, заданным непосредственно как в vprintf.

- Функция: int vasprintf (char **ptr, const char *template, va_list ap)

Функция vasprintf - эквивалент asprintf с переменным списком параметров, заданным непосредственно как и для vprintf.

- Функция: int obstack_vprintf (struct obstack *obstack, const char* template, va_list ap)

Функция obstack_vprintf - эквивалент obstack_printf с переменным списком параметров, заданным непосредственно как для vprintf.

Ниже приводится пример, показывающий, как Вы могли бы использовать vfprintf. Это - функция, которая выводит сообщения об ошибках в поток stderr, вместе с префиксом, указывающим имя программы (см. Раздел 2.3 [Сообщения об ошибках], описание program_invocation_short_name).

#include <stdio.h> #include <stdarg.h> void eprintf (const char *template, ...) { va_list ap; extern char *program_invocation_short_name; fprintf (stderr, '%s: ', program_invocation_short_name); va_start (ap, count); vfprintf (stderr, template, ap); va_end (ap); }
Вы могли бы вызывать eprintf например так:
eprintf ('file `%s' does not exist\n', filename);

Синтаксический разбор Строки Шаблона

Вы можете использовать функцию parse_printf_format, для получения информации относительно числа и типов аргументов, которые ожидаются данной строкой шаблона. Эта функция вызывает интерпретаторы, которые обеспечивают интерфейс в printf во избежание передачи недопустимых аргументов.

Все символы, описанные в этом разделе объявлены в файле заголовков 'printf.h'.

- Функция: size_t parse_printf_format (const char *template, size_t n, int* argtypes)

Эта функция возвращает информацию относительно числа и типов аргументов, ожидаемых printf в строке шаблонов. Информация сохраняется в массиве argtypes; каждый элемент этого массива описывает один аргумент. Эта информация предоставляется в виде различных 'PA_' макрокоманд, которые перечисляются ниже.

Аргумент n определяет число элементов в массиве argtypes. Это наибольшее число элементов, которые parse_printf_format пробует написать.

Parse_printf_format возвращает общее число аргументов, требуемых шаблоном. Если это число больше n, то возвращаемая информация, описывает только первые n аргументов. Если Вы хотите получить информацию относительно большего, чем n, числа аргументов, зарезервируйте больший массив и вызовете parse_printf_format снова.

Типы аргумента закодированы как комбинация базисного типа и битов флага модификатора.

- Макрос: int PA_FLAG_MASK

Эта макрокоманда - маска для битов флага модификатора типа. Вы можете написать выражение (argtypes [i] & PA_FLAG_MASK) чтобы извлечь только биты флага для аргумента, или (argtypes [i] & ~PA_FLAG_MASK) чтобы извлечь только базисный код типа.

Имеются символические константы, которые представляют базисные типы; они устанавливаются для значений integer.

PA_INT Определяет, что исходный тип - int. PA_CHAR Определяет, что исходный тип - int, приведеннное к char. PA_STRING Определяет, что исходный тип - char *, строка с нулевым символом в конце. PA_POINTER Определяет, что исходный тип - void *, произвольный указатель. PA_FLOAT Определяет, что исходный тип с плавающей точкой. PA_DOUBLE Определяет, что исходный тип - double. PA_LAST Вы можете определять дополнительные исходные типы для ваших собственных программ как смещения из PA_LAST.
Например, если у Вас определены типы данных 'foo' и 'bar' с их собственными специализированными форматами для printf, то Вы можете определять эти типы как:
#define PA_FOO PA_LAST #define PA_BAR (PA_LAST + 1)
Имеются биты флага, которые изменяют базисный тип. Они объединены с кодом для базисного типа, используя операцию или.
PA_FLAG_PTR

Если этот бит устанавливается, это указывает, что закодированный тип - указатель на исходный тип, а не непосредственное значение.

Например, 'PA_INT | PA_FLAG_PTR' представляет тип `int *'.

PA_FLAG_SHORT

Если этот бит устанавливается, это указывает, что исходный тип изменяется как short. (Соответствует модификатору типа 'h'.)

PA_FLAG_LONG

Если этот бит устанавливается, это указывает, что исходный тип изменяется как long. (Соответствует модификатору типа 'l'.)

PA_FLAG_LONG_LONG

Если этот бит устанавливается, это указывает, что исходный тип изменяется как long long.

PA_FLAG_LONG_DOUBLE

Это - синоним для PA_FLAG_LONG_LONG, используемого обычно с исходным типом PA_DOUBLE для обозначения типа long double.

Пример Синтаксического анализа Строки Шаблона

/* Проверка, является ли NARGS, определяющий объекты вектора ARGS формату строки FORMAT: если да, возвращает 1. в противном случае возвращает 0 после вывода сообщения об ошибке */ int validate_args (char *format, int nargs, OBJECT *args) { int *argtypes; int nwanted; /* Получить информацию об аргументах. Каждый спецификатор формата должен быть длиной по крайней мере в два символа, поэтому не может быть спецификаторов длиной длиной больше половины длины строки. */ argtypes = (int *) alloca (strlen (format) / 2 * sizeof (int)); nwanted = parse_printf_format (string, nelts, argtypes); /* Проверим количество аргументов */ if (nwanted > nargs) { error ('too few arguments (at least %d required)', nwanted); return 0; } /* Проверим тип, требуемый для каждого аргумента, и соответствие ему данного аргумента. */ for (i = 0; i < nwanted; i++) { int wanted; if (argtypes[i] & PA_FLAG_PTR) wanted = STRUCTURE; else switch (argtypes[i] & ~PA_FLAG_MASK) { case PA_INT: case PA_FLOAT: case PA_DOUBLE: wanted = NUMBER; break; case PA_CHAR: wanted = CHAR; break; case PA_STRING: wanted = STRING; break; case PA_POINTER: wanted = STRUCTURE; break; } if (TYPE (args[i]) != wanted) { error ('type mismatch for arg number %d', i); return 0; } } return 1; }

7.10 Настройка printf

Библиотека GNU C позволяет Вам определять ваши собственные спецификаторы преобразования для строк шаблона printf, т.е. научить printf выводить важные структуры данных вашей программы так, как Вам этого хочется.

Это можно сделать, указав формат преобразования с помощью функции register_printf_function; см. Раздел 7.10.1 [Указание Новых Форматов Вывода]. Один из аргументов, которые Вы передаете этой функции ­ указатель на функцию обработчика, которая производит фактический вывод; за более подробной информацией о написании этой функции обращайтесь к Разделу 7.10.3 [Определение Обработчика Вывода].

Вы можете также установить функцию, которая возвращает информацию относительно числа и типа аргументов, ожидаемых спецификатором формата преобразования. См. Раздел 7.9.10 [Синтаксический анализ Строки Шаблона], для получения дополнительной информации об этом.

Средства этого раздела объявлены в файле 'printf.h'.

Примечание о Переносимости: возможность изменения синтаксиса printf строк шаблона - расширение GNU. Стандарт ANSI C не имеет ничего подобного.

Указание Новых Форматов Вывода

Функция для регистрирования новых преобразований вывода register_printf_function, объявлена в `printf.h'.

- Функция: int register_printf_function(int SPEC,printf_function HANDLER_FUNCTION, printf_arginfo_function ARGINFO_FUNCTION)

Эта функция определяет символ спецификатора преобразования SPEC.

Так, если SPEC равен 'q', то определяется модификатор '%q'.

HANDLER_FUNCTION - функция, вызываемая printf, когда это модификатор появляется в строке шаблона. См. Раздел 7.10.3 [Определение Обработчика Вывода], для уточнения информации относительно того, как определить функцию в качестве этого аргумента. Если Вы задаете пустой указатель, существующая функция обработчика для спецификаций удаляется.

Arginfo_function - функция, вызываемая parse_printf_format, когда это преобразование появляется в строке шаблона. См. Раздел 7.9.10 [Синтаксический анализ Строки Шаблона]. Обычно Вы устанавливаете обе функции обработки вывода одновременно, но если Вы никогда не обращаетесь к parse_printf_format, Вы не должны определять функцию arginfo_function.

Возвращаемое значение - 0 в случае успеха, и -1 при сбое (который происходит, если спецификации находятся вне диапазона).

Вы можете переопределять форматы стандартного вывода, но это ­ возможно не лучшая идея из-за потенциального путаницы. Если Вы это сделаете, могут пострадать библиотечные подпрограммы, написанные другими людьми.

Ключи Спецификатора Преобразования

Если Вы определяете какое-либо значение для '%q',то что произойдет, если шаблон содержит '%+23q' или '%-#q'? Чтобы реализовать обработку этого, обработчик должен быть способен получить ключи, определенные в шаблоне.

Оба аргумента функции register_printf_function - HANDLER_FUNCTION и ARGINFO_FUNCTION получает в качестве аргумента типа struct printf_info, который содержит информацию относительно ключей, появляющихся в образце спецификатора формата вывода. Этот тип данных объявлен в заглавном файле 'printf.h'.

- Тип: struct printf_info

Эта структура используется для передачи информации о ключах, появляющихся в образце спецификатора формата вывода в printf строке шаблона в обработчик и функции arginfo для их спецификатора. Она содержит следующие элементы:

'int prec'

Эта переменная содержит задаваемую точность. Значение -1, если никакая точность не была определена. Если, точность была задана как '*', структура printf_info, переданная в функцию обработчика, содержит фактическое значение, взятое из списка параметров. Но структура, переданная в функция arginfo содержит значение INT_MIN, так как фактическое значение не известно.

'int width'

Эта переменная содержит задаваемую минимальную ширину поля вывода. Значение 0, если ширина не была определена. Если, ширина поля была задана как '*', структура printf_info, переданная в функцию обработчика, содержит фактическое значение, взятое из списка параметров. Но структура, переданная в функция arginfo содержит значение INT_MIN, так как фактическое значение не известно.

'сhar spec'

Эта переменная содержит заданный символ спецификатора формата вывода. Он содержится в структуре для того, чтобы Вы могли указать одну и ту же функцию обработчика для различных символов, но при этом иметь возможность их различать при вызове функции обработчика.

'unsigned int is_long_double'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'L' был определен.

'unsigned int is_short'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'h' был определен.

'unsigned int is_long'

Это - логическая переменная, которая содержит значение истина, если модификатор типа 'l' был определен.

'unsigned int alt'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '#'.

'unsigned int space'

Это - логическая переменная, которая содержит значение истина, если был определен флаг ' '.

'unsigned int left'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '-'.

'unsigned int showsign'

Это - логическая переменная, которая содержит значение истина, если был определен флаг '+'.

'char pad'

Это - символ, использующийся для дополнения вывода в минимальную ширину поля. Значение - '0' если был определен определен флаг '0' , иначе ' '.

Определение Обработчика Вывода

Теперь рассмотрим, как определить функцию обработчика и функции arginfo, которые передаются как аргументы для register_printf_functi­ on.

Вы должны определить ваши функции обработчика с прототипом следу­ ющим образом:

int function (FILE *stream, const struct printf_info *info, va_list *ap_pointer)

Аргумент stream, переданный функции обработчика - это поток, в который она должна записать вывод.

Аргумент info - указатель на структуру, которая содержит информацию относительно различных ключей, которые были включены в строку шаблона. Вы не должны изменять эту структуру внутри вашей функции обработчика. См. Раздел 7.10.2 [Опции Спецификатора Преобразования], для описания этой структуры данных.

Аргумент ap_pointer используется для передачи хвоста списка параметров, содержащего значения котторые Ваш обработчик должен напечатать. В отличие от большинства других функций, которым может быть передан явный список параметров, здесь передается указатель на va_list, а не сам va_list. Таким образом, Вы должны обрабатывать аргументы с помощью va_arg(TYPE, * ap_pointer).

( Введение указателя здесь позволяет указать функцию, которая вызывает вашу функцию обработчика, чтобы модифицировать собственную переменную va_list, для обновления информации о параметрах, которые ваш обработчик обрабатывает. См. Раздел A. 2 [Variadic (функция)] 2.)

Ваша функция обработчика должна возвратить значение точно так же как это делает printf: она должна возвратить число символов, которое она написала, или отрицательное значение, чтобы указать ошибку.

-Тип данных: printf_function

Это тип данных, который должна иметь функция обработчика. Если Вы собираетесь использовать parse_printf_format в вашем приложении, то Вы должны также определить функцию, являющуюся параметром arginfo_function для каждого нового формата, который Вы устанавливаете с помощью register_printf_function.

Вот прототип подобной функции:

int function (const struct printf_info *info,size_t n,int *argtypes);

Функция должна возвращать число параметров, которые обрабатывает формат вывода. Кроме того, функция не должна заполнять больше, чем n элементов argtypes массива с информацией относительно типов каждого из этих параметров. Эта информация закодирована с помощью 'PA_' макрокоманд. (Обратите внимание, что это - то же самое что и соглашение о вызовах parse_printf_format.)

-Тип данных: printf_arginfo_function

Этот тип используется, для описания функций, которые возвращают информацию о числе и типе параметров, используемых спецификатором формата вывода.

t

Пример Расширения Printf

Вот пример, показывающий, как определять printf функцию обработчика. Эта программа определяет структуру данных называемую Widget и определяет формат '%M', для печати информации о параметре Widget* включая значение указателя и имя, содержащееся в структуре данных. Формат вывода '%W' поддерживает минимальную ширину поля и опции левого выравнивания, но игнорирует все остальные.

#include <stdio.h> #include <printf.h> #include <stdarg.h> typedef struct { char *name; } Widget; int print_widget (FILE *stream, const struct printf_info *info, va_list *app) { Widget *w; char *buffer; int len; /* Преобразуем выходную информацию в строку. */ w = va_arg (*app, Widget *); len = asprintf (&buffer, '<Widget %p: %s>', w, w->name); if (len == -1) return -1; /* Заполняем поле минимальной длины и выводим в поток.*/ len = fprintf (stream, '%*s', (info->left ? - info->width : info->width), buffer); /* Сброс и возврат. */ free (buffer); return len; } int main (void) { / * Создаем widget, который необходимо напечатать. * / Widget mywidget; mywidget.name = 'mywidget'; / * Теперь печатаем widget. * / printf ('|%W|\n', &mywidget); printf ('|%35W|\n', &mywidget); printf ('|%-35W|\n', &mywidget); return 0; }
Программа выводит:
|<Widget 0xffeffb7c: mywidget>| | <Widget 0xffeffb7c: mywidget>| |<Widget 0xffeffb7c: mywidget> |

7.11 Форматируемый Ввод

Функции, описанные в этом разделе (scanf и ей подобные) обеспечивают средства для форматируемого ввода, аналогичного форматируемым средствам вывода. Эти функции обеспечивают механизм для чтения произвольных значений при контроле над строкой формата или строкой шаблона.

Основы Форматируемого Ввода

Вызов scanf на первый взгляд подобен обращениям к printf, в котором произвольные параметры считываются под управлением строки шаблона. В то время, как синтаксис спецификаций преобразования в шаблоне очень схож, синтаксис для printf, интерпретация шаблона ориентируется больше на ввод свободного формата и простое сопоставление с образцом, а не форматирование устанавливаемого поля. Например, большинство scanf пропускают любое преобразование над каким-нибудь количеством 'пробельных символов' (включая пробел, метки табуляции, и символы перевода строки) во входном файле, и нет никакого понятия точности для числовых входных преобразований которое имеется для соответствующих преобразований вывода. Обычно, непробельные символы в шаблоне, как ожидается, будут точно соответствовать символам во входном потоке, но соответствующая ошибка отличается от входной ошибки в потоке.

Другая отличие между scanf и printf - то, что Вы должны не забыть обеспечивать указатели а не непосредственные значения как необязательные параметры scanf; значения, которые читаются, сохраняются в объектах, на которые указывают указатели. Даже опытные программисты имеют тенденцию забывать это иногда, так если ваша программа получает странные ошибки, которые, кажется, связаны со scanf, Вам следует дважды проверить это.

Когда происходит ошибка несоответствия , scanf немедленно прерывается, оставляя первый символ несоответствия как следующий символ, который нужно читать из потока. Нормальное возвращаемое значение из scanf - число значений, которые были назначены, так что Вы можете использовать это, чтобы определить, случалась ли ошибка соответствия прежде, чем все прочитались ожидаемые значения.

Функция scanf обычно используется для программ вроде считывания содержания таблиц. Ниже приводится функция, которая использует scanf для того, чтобы инициализировать массив элементов double:

void readarray (double *array, int n) { int i; for (i=0; i<n; i++) if (scanf (' %lf', &(array[i])) != 1) invalid_input_error (); }
Функции форматируемого ввода используются не так часто как функции форматируемого вывода.

Если Вы пытаетесь считывать входную информацию, которая не соответствует ни простому, фиксированному шаблону, то может быть Вам лучше использовать средства типа Flex, чтобы сгенерировать лексический сканер, или Bison, чтобы сгенерировать синтаксический анализатор, а не использовать scanf. Для получения более подробной информации, см. раздел 'Flex' в Flex: Лексический Генератор Ввода и раздел 'Bison' в Справочном описании Bison.

Синтаксис Входных Форматов

Строка шаблона для scanf это строка, которая содержит обычные многобайтовые символы со спецификациями преобразования, которые начинаются с '%'.

Любой пробельный символ (как определено функцией isspace; см. Раздел 4.1 [Классификация Символов]) в шаблоне заставляет любое число символов промежутка во входном потоке читаться и отбрасываться. Символы промежутка, которые согласованы могут быть не точно те же самые символы промежутка которые появляются в строке шаблона. Например, добавьте ',' к шаблону, чтобы распознать запятую с любым количеством пробелов до и после нее.

Другие символы в строке шаблона, которые не являются частью спецификаций преобразования, должны точно соответствовать символам во входном потоке; если дело обстоит не так, возвращается ошибка несоответствия.

Спецификации преобразования в строке scanf шаблона имеют общую форму:

% FLAGS WIDTH TYPE CONVERSION
Более подробно, входная спецификация преобразования состоит из начального символа '%', сопровождаемого последовательностью:

Таблица Входных Преобразований

Ниже приводится таблица, которая содержит различные спецификации форматов:

'%d'

Соответствует необязательно целому числу со знаком, которое записано в десятичном виде. См. Раздел 7.11.4 [Входные Форматы Чисел].

'%i'

Соответствует необязательно целому числу со знаком в любом из форматов, которые Язык C определяет для определения константы integer. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%o'

Соответствует integer unsigned, который записан в восьмеричной системе счисления. См. Раздел 7.11.4 [Числовые Входные Форматы].

'%u'

Соответствует беззнаковому целому, записанному в десятичной системе счисления. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%x', '%X'

Соответствуют беззнаковому целому, записанному в шестнадцатеричной системе счисления. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%e', '%f', '%g', '%E', '%G'

Соответствуют необязательному знаковому числу с плавающей запятой. См. Раздел 7.11.4 [Числовые Входные Преобразования].

'%s'

Соответствует строке, содержащей только символы непромежутка. См. Раздел 7.11.5 [Входные Преобразования Строки].

'%['

Соответствует строке символов, которые принадлежат заданному множеству. См. Раздел 7.11.5 [Входные Преобразования Строки].

'%c'

Соответствует строке из одного или большего количества символов; чтение числа символов управляется максимальной шириной поля, заданной для формата.

'%p'

Соответствует значению указателя в том же самом определенном реализацией формате, используемом форматом вывода printf '%p'. См. Раздел 7.11.7 [Другие Входные Форматы].

'%n'

Это преобразование не читает никакие символы; оно записывает число прочитанных символов. См. Раздел 7.11.7 [Другие Входные Преобразования].

'%%'

Это соответствует символу `%' во входном потоке. См. Раздел 7.11.7 [Другие Входные Преобразования]. Если синтаксис спецификации преобразования является недопустимым, поведение неопределено. Если не достаточно аргументов функции, чтобы обнспечить адреса для всех спецификаций формата в строках шаблона, которые выполняют назначения, или если аргументы имеют неправильные типы, поведение также неопределено. С другой стороны, дополнительные аргументы просто игнорируются.

Числовые Входные Преобразования

Этот раздел описывает преобразования scanf для чтения числовых значений.

Формат ввода '%d' соответствует необязательно целому числу со знаком в десятичной системе счисления. Синтаксис - такой же как и для функции strtol (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) со значением 10 для основного аргумента.

Формат ввода '%i' соответствует необязательному целому числу со знаком в любом из форматов, которые Язык C определяет для целой константы. Синтаксис - такой же как для функции strtol (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) со значением 0 для основного аргумента. (Вы можете печатать integer в этом синтаксисе через printf, используя флаг '*' с форматами '%x', '%o', или '%d'. См. Раздел 7.9.4 [Целочисленные Преобразования].)

Например, любая из строк '10', '0xa', или '012' может cчитываться как целая константа при преобразовании '%i'. Каждая из этих строк определяет число с десятичным значением 10.

Форматы ввода '%o', '%u', и '%x' соответствуют беззнаковому целому в восьмеричном, десятичном, и шестнадцатеричном форматах соответственно. Синтаксис - такой же как для функции strtoul (см. Раздел 14.7.1 [Синтаксический анализ Целых чисел] ) с соответствующим значением (8, 10, или 16) для основного аргумента.

Формат '%X' идентичен формату '%x'. Оба они разрешают использовать как цифры или символы верхнего регистра или символы нижнего регистра.

Заданный по умолчанию тип соответствующего аргумента форматов '%d' и '%i' является 'int *', и 'unsigned int *' для других целочисленных форматов. Вы можете использовать следующие модификаторы типа, чтобы задать другие размеры integer:

Все входные форматы '%e', '%f', '%g', '%E', и '%G' взаимозаменяемы. Они все соответствуют необязательному знаковому числу с плавающей запятой, в том же самом синтаксисе как для функции strtod (см. Раздел 14.7.2 [Синтаксический анализ Чисел с Плавающей Запятой] ).

Для преобразований с плавающей запятой, заданный по умолчанию тип аргумента - float *. (Это отличается от соответствующих преобразований вывода, где заданный по умолчанию тип - double; не забудьте, что аргументы float в printf преобразовываются в double заданными по умолчанию поддержками аргумента, но float * аргументы не преобразуются в double *.) Вы можете задавать другие размеры float, используя следующие модификаторы типа:

Строковые Входные Преобразования

Этот раздел описывает преобразования ввода scanf для чтения строковых и символьных значений: '%s', '%[', и '%c'.

Имеется два способа получить ввод от этих преобразований:

Формат '%c' (самое простое): оно соответствует фиксированному числу символов. Если Вы не задаете максимум длины поля ввода, значение по умолчанию 1. Это преобразование не добавляет пустой символ в конец текста, которую оно читает. Оно также не пропускает начальные пробельные символы. Оно читает ровно n символов, и выдает ошибку, если оно не может их получить. Так как при '%c' всегда имеется максимальная ширина поля (заданная, или 1 по умолчанию), Вы всегда можете предотвратить переполнение, делая буфер достаточно большим.

Формат '%s' соответствует строке непробельных символов. Оно пропускает и отбрасывает начальные пробельные символы, но останавливается, когда сталкивается с пробельным символом когда уже что-то считано. Он сохраняет пустой символ в конце текста, который считывает. Например, при считывании:

hello, world
формат '%10c' производит 'hello, wo', а считывании того же самого с форматом '%10s' производит 'hello,'.

Предупреждение: если Вы не задаете ширину поля для '%s', то число символов, ограничено только тем, где появится следующий пробельный символ. Это конечно означает что недопустимый ввод может вызвать ошибку.

Чтобы считывать символы, которые принадлежат произвольному набору по вашему выбору, используйте формат '%['. Вы определяете набор между символом `[' и следующим символом `]', используя синтаксис, используемый в регулярных выражениях. Частные случаи:

'%[' преобразование не перескакивает начальные пробельные символы. Ниже приводятся некоторые примеры форматов '%[' преобразования и что они означают:
`%25[1234567890]'
Соответствует строке до 25 цифр.
`%25[][]'
Соответствует строке до 25 квадратных скобок.
`%25[^ \f\n\r\t\v]'
Соответствует строке до 25 символов, которая не содержит любых из стандартных пробельных символов. Это немного отличается от '%s', т. к., если ввод начинается с символа пропуска, '%[' возвращает ошибку несоответствия, в то время как '%s' просто отбрасывает начальный пропуск.
`%25[a-z]'
Соответствует до 25 символам нижнего регистра. Еще одно напоминание: форматы '%s' и '%[' опасны, если Вы не определяете максимальную ширину или используете флаг 'a', т. к. слишком длинный ввод переполнит любой буфер. Хорошо написанная программа сообщает о недопустимом вводе сообщением об ошибках, а не завершается аварийным отказом.

Динамическое Распределение Форматов Строки

Расширение GNU для форматируемого ввода позволяет Вам безопасно считывать строку безо всякого указания максимального размера. При использовании этой возможности, Вы не указываете буфер; а вместо этого, scanf распределяет буфер, достаточно большой, чтобы содержать данные и возвращает Вам его адрес. Чтобы использовать эту возможность, укажите 'а' в качестве флага, например '%as' или '%a[0-9a-z]'.

Аргумент указателя, который Вы обеспечиваете для размещения входной информации, должен иметь тип char **. Функция scanf резервирует буфер и сохраняет адрес там, куда указывает аргумент. Вы должны освободить буфер функцией free, когда Вы больше не нуждаетесь в нем.

Вот пример использования флага 'а' со спецификацией преобразования '%[...]' для чтения 'переменного назначения' в форме 'переменная = значение'.

{ char *variable, *value; if (2 > scanf ('%a[a-zA-Z0-9] = %a[^\n]\n', &variable, &value)) { invalid_input_error (); return 0; } ... }

Другие Входные Форматы

Этот раздел описывает разнообразные входные форматы.

Формат '%p' используется, для того чтобы считывать значение указателя. Оно распознает тот же самый синтаксис, как и формат вывода '%p' для printf (см. Раздел 7.9.6 [Другие Преобразования Вывода]). Соответствующий параметр должен иметь тип void **; то есть адрес места, где разместить указатель.

Формат '%n' возвращает число прочитанных символов. Соответствующий параметр должен иметь тип int *. Это преобразование работает таким же образом как и формат '%n' для printf; см. примеры в Разделе 7.9.6 [Другие Форматы Вывода].

Формат '%n' - единственный механизм для определения успеха буквального соответствия или преобразования с подавляемыми назначениями. Если '%n' следует за ошибкой несоответствия, scanf возвратится, не успев обработать '%n'. Если Вы поместите -1 в этот параметр перед вызовом scanf, присутствие -1 после вызова scanf указывает, что ошибка произошла перед обработкой '%n'.

В заключение, формат '%%' соответствует символу `%' во входном потоке, без использования параметра. Это преобразование не позволяет определение никаких флагов, ширины поля, или модификаторов типа.

Форматируемые Входные Функции

Ниже приводятся описания функций для выполнения форматируемого ввода. Прототипы для этих функций находятся в файле 'stdio.h'.

-Функция: int scanf (const char *template, ...)

Функция scanf читает форматируемый ввод из потока stdin под управлением строки шаблона. Необязательные параметры - указатели на места, которые получают возникающие в результате значения.

Возвращаемое значение - обычно число успешных соответствий. Если условие конца файла обнаружено перед любым соответствием (включая соответствие пробельным символам и литеральных символов в шаблоне), то возвращается EOF.

-Функция: int fscanf (FILE *stream, const char *template, ...)

Эта функция - аналог scanf, за исключением того, что ввод осуществляется из вместо stdin указанного потока.

-Функция: int sscanf (const char *s, const char *template, ...)

Подобна scanf, за исключением того, что символы берутся из строки s с нулевым символом в конце, а не из потока. Достижение конца строки обрабатывается как условие конца файла.

Поведение этой функции неопределено, если копирование происходит между объектами, которые пересекаются, например, если s задан еще и как аргумент для получения считанной строки под управлением формата '%s'.

Функции Ввода С Переменными Аргументами

Функции vscanf и ей подобные работают так, чтобы Вы могли определять ваши собственные scanf-подобные функции, которые используют ту же самую внутреннюю организацию как встроенные форматируемые функции вывода. См. Раздел 7.9.9 [Вывод Аргументов Переменной].

Примечание о Переносимости: функции, перечисленные в этом разделе являются расширением GNU.

-Функция: int vscanf (const char *template, va_list ap)

Эта функция похожа на scanf за исключением того, что вместо того, чтобы принимать переменное число аргументов непосредственно, она берет указатель на список параметров - ар типа va_list (см. Раздел A.2 Variadic Функции] ).

-Функция: int vfscanf (FILE *stream,const char *template,va_list ap)

Эта функция - эквивалент fscanf с переменным списком параметров, заданным непосредственно как для vscanf.

7.12 Блочный Ввод-Вывод

Этот раздел описывает операции ввода и вывода на блоках данных. Вы можете использовать эти функции для чтения и записи двоичных данных, также как читать и писать текст блоками устанавливаемого размера - а не символами или строками.

Сохранение данных в двоичной форме часто значительно более эффективно чем использование форматируемых функций ввода - вывода. Также, для чисел с плавающей запятой, двоичная форма избегает возможной потери точности в процессе преобразования. С другой стороны, двоичные файлы не могут быть легко исследованы или изменяться, используя много стандартных файловых утилит (вроде текстовых редакторов), и не переносимы между различными реализациями языка, или различными видами компьютеров.

Эти функции объявлены в ' stdio.h '.

-Функция:size_t fread (void *data, size_t size, size_t count, FILE *stream)

Эта функция читает до count объектов размера size в массив. Она возвращает число прочитанных объектов, которое может быть меньше чем count, если происходит ошибка чтения, или достигнут конец файла. Эта функция возвращает значение нуль (и ничего не читает) если или size или count равен нулю.

Если fread достигает конца файла в середине объекта, она возвращает номер прочитанных полностью объектов, и отбрасывает несчитанные до конца.

-Функция: size_t fwrite (const void *data, size_t size, size_t count, FILE *stream)

Эта функция записывает до count объектов из массива данных в указанный поток. Возвращаемое значение - обычно count, если считывание успешно. Любой другое значение указывает какую-либо ошибку, например нехватку памяти.

КОНЕЦ ФАЙЛА и Ошибки

Многие из функций, описанных в этой главе возвращают значение макрокоманды EOF, указывающей неудачное завершение операции. Когда используется EOF, для сообщения о конце файла или о случайной ошибке, часто лучше использовать функцию feof, чтобы явно проверить конец файла и ferror, чтобы проверить наличие ошибки. Это контрольные индикаторные функции, которые являются частью внутреннего состояния объекта потока, индикаторы устанавливаются если соответствующее условие было обнаружено предыдущей операцией ввода - вывода на этом потоке.

Эти символы объявлены в заглавном файле ' stdio.h '.

- Макрос: int EOF

Этот макрос имеет целое значение, которое возвращается рядом функций, чтобы указать условие конца файла, или какую-нибудь другую ошибку. В библиотеке GNU, EOF имеет значение -1. В других библиотеках, значением может быть некоторое другое отрицательное число.

-Функция: void clearerr (FILE *stream)

Эта функция очищает индикаторы конца файла и ошибки для указанного потока. Позиционирующие файл функции (см. Раздел 7.15 [Позиционирование Файла]) также, очищают индикатор конца файла для потока.

-Функция: int feof (FILE *stream)

Функция feof возвращает отличное от нуля число, только если установлен индикатор конца файла для потока.

-Функция: int ferror (FILE *stream)

Функция ferror возвращает отличное от нуля число, только если индикатор ошибки для потока установлен, указывая что ошибка произошла на предыдущей операции на потоке.

В дополнение к установке индикатора ошибки, связанного с потоком, функции, которые работают с потоками, устанавливают errno таким же образом как соответствующие функции низкого уровня, которые работают с дескрипторами файла. Например, все функции, которые выполняют вывод в поток, такие как fputc, printf, и fflush, осуществлены в терминах записи, и все условия ошибки errno, определенные для записи, имеют значение и для этих функций. Для получения более подробной информации о функциях ввода - вывода на уровне дескрипторов, см. Главу 8 [ввод - вывод низкого уровня] 3.

7.13 Текстовые и Двоичные Потоки

Система GNU и другие posix-совместимые операционные системы организовывает все файлы как однородные последовательности символов. Однако, некоторые другие системы делают различие между файлами, содержащими текст и файлами, содержащими двоичные данные, и средства ввода и вывода ANSI C предусматривают это различие. Этот раздел сообщает Вам, как написать программы, переносимые на такие системы.

Когда Вы открываете поток, Вы можете определять или текстовый поток или двоичный поток. Вы указываете, что Вы хотите двоичный поток, определяя модификатор 'b' в параметре opentype для fopen; см. Раздел 7.3 [Открытие Потоков]. Без этой опции, fopen открывает файл как текстовый поток.

Текстовые и двоичные потоки имеют различия:

Так как двоичный поток более общий и более предсказуемый чем текстовый поток, Вы могли бы задаться вопросом, в чем цель текстовых потоков. Почему не всегда используют двоичные потоки? Ответ в том, что на разных операционных системах, текстовые и двоичные потоки используют различные форматы файла, и единственный способ читать или записать 'обычный текстовый файл' который может работать с другими ориентируемыми текстом программами - через текстовый поток.

В библиотеке GNU, и на всех POSIX системах, не имеется никакого различия между текстовыми потоками и двоичными потоками. Когда Вы открываете поток, Вы получаете тот же самый вид потока, даже если Вы заказывали двоичный. Этот поток может обрабатывать любое содержание файла, и не имеет ни каких ограничений, которые в отличие от текстовые потоков.

7.14 Позиционирование Файла

Позиция файла потока описывает, где в файле поток в настоящее время читает или производит запись. Ввод-вывод на потоке продвигает позицию файла через весь файл. В системе GNU, позиция файла представляется как целое число, которое содержит число байтов от начала файла. См. Раздел 6.1.2 [Позиция Файла].

В течение ввода-вывода в обычный дисковый файл, Вы можете менять позицию в файле всякий раз, когда Вы желаете читать или записывать в любую часть файла. Некоторые другие виды файлов также позволяют делать это. Файлы, которые поддерживают изменение позиции файла иногда упоминается как файлы прямого доступа.

Вы можете использовать функции в этом разделе, чтобы исследовать или изменить индикатор позиции файла, связанный с потоком. Символы, перечисленные ниже объявлены в заглавном файле 'stdio.h'.

-Функция: long int ftell (FILE *stream)

Эта функция возвращает текущую позицию файла указанного потока.

Эта функция может выдать ошибку, если поток не поддерживает позиционирование файла, или если позиция файла не может представляться как long int, или возможно по другим причинам.

Если происходит ошибка, возвращаемое значение -1.

-Функция: int fseek (FILE *stream, long int offset, int whence)

Функция fseek используется для изменения позиции файла указанного потока. Значение whence должно быть одной из констант SEEK_SET, SEEK_CUR, или SEEK_END, т. е. указывать является ли смещение относительно начала файла, текущей позиции файла, или конца файла, соответственно. Эта функция возвратит нуль, если операция была успешна, и значение, отличное от нуля чтобы указать отказ.

Успешное обращение также очищает индикатор конца файла потока и отбрасывает любые символы, которые были 'помещены обратно' использованием ungetc. Fseek дописывает любой буферизированный вывод перед позиционированием файла, или еще запоминает его, так что он будет записан позже в соответствующем месте файла.

Примечание о Переносимости: В не-posix системах, ftell и fseek могут работать надежно только на двоичных потоках. См. Раздел 7.14 [Двоичные Потоки].

Следующие символические константы определены для использования в качестве аргумента whenceа для fseek. Они также используются функцией lseek (см. Раздел 8.2 [Примитивы ввода - вывода] ) и для указания смещения для блокировок файла (см. Раздел 8.7 [Операции Управления] ).

-Макрос: int SEEK_SET

Это целая константа которая, когда используется как аргумент whence функции fseek и определяет, что смещение указывается относительно начала файла.

-Макрос: int SEEK_CUR

Это целая константа которая используется как аргумент whence функции fseek и определяет, что смещение указывается относительно текущей позиции файла.

-Макрос: int SEEK_END

Это целая константа которая используется как аргумент whence функции fseek и определяет, что смещение указывается относительно конца файла.

-Функция: void rewind (FILE *stream)

Функция rewind позиционирует указанный поток в начало файла. Это эквивалентно вызову fseek на потоке с аргументом смещения 0L и аргументом whence SEEK_SET, за исключением того, что возвращаемое значение отбрасывается, и индикатор ошибки для потока сброшен.

Эти три побочных результата исследования для констант 'SEEK_...' существуют ради совместимости с более старыми BSD системами. Они определены в двух различных файлах: 'fcntl.h' и 'sys/file.h'.

L_SET синоним SEEK_SET.

L_INCR синоним SEEK_CUR.

L_XTND синоним SEEK_END.

7.15 Переносимые Функции позиционирования файла

В системе GNU, позиция файла - просто символьный счетчик. Вы можете задавать любое значение count как аргумента в fseek и получать надежные результаты для любого файла произвольного доступа. Однако, некоторые ANSI C системы не представляет позиции файла таким образом,.

На некоторых системах, где текстовые потоки отличаются от двоичных потоков невозможно представить позицию файла текстового потока как счетчик символов от начала файла. Например, позиция файла на некоторых системах должна кодировать, и смещение записи внутри файла, и смещение символа внутри записи.

Как следствие, если Вы хотите чтобы ваши программы были переносимы на эти системы, Вы должны соблюдать некоторые правила:

Но даже если Вы соблюдаете эти правила, Вы можете все еще иметь проблемы длинными файлами, т. к. ftell и fseek используют значение int long, для представления позицию файла. Этот тип может не иметь участка памяти, для кодирования всех позиций файла в большом файле.

Так, если Вы хотите поддерживать системы со специфическими кодированием для позиций файла, то лучше использовать функции fgetpos и fsetpos. Эти функции представляют позицию файла, используя тип данных fpos_t, чье внутреннее представление меняется от системы к системе.

Эти символы объявлены в заглавном файле ' stdio.h '.

-Тип данных: fpos_t

Это - тип объекта, который может кодировать информацию относительно файловой позиции потока, для использования функциями fgetpos и fsetpos.

В системе GNU, fpos_t эквивалентен off_t или long int. В других системах, он может иметь различное внутреннее представление.

-Функция: int fgetpos (FILE *stream, fpos_t *position)

Эта функция сохраняет значение индикатора файловой позиции для указанного потока в указанном объекте fpos_t. Обращение успешно если fgetpos возвращает нуль; иначе она возвращает значение отличное от нуля и сохраняет определенное реализацией положительное значение в errno.

-Функция: int fsetpos (FILE *stream, const fpos_t position)

Эта функция устанавливает индикатор файловой позиции для указанного потока в позицию position, которая должна определяться предыдущим обращением к fgetpos на том же самом потоке. Если обращение успешно, fsetpos очищает индикатор конца файла на потоке, отбрасывает любые символы, которые были 'помещены обратно' использованием ungetc, и возвращает значение нуля. Иначе, fsetpos возвращает значение отличное от нуля и сохраняет определенное реализацией положительное значение в errno.

7.16 Буферизация Потока

Символы, которые записаны в поток, обычно накапливаются и передаются в файл блоками асинхронно, вместо того, чтобы появляться, как только они выводятся прикладной программой. Аналогично, потоки часто восстанавливают ввод из главной среды в блоках а не по принципу символ-за-символ. Это называется буферизацией.

Если Вы напишете программы, которые делают интерактивный ввод и вывод используя потоки, Вы должны знать, как работает буферизация, когда Вы разрабатываете интерфейс пользователя в вашей программе. Иначе, вывод (типа подсказки) может не появиться, как ожидалось, и т. д.

Этот раздел имеет дело только с управлением передачей символов между потоком и файлом или устройством.

Вы можете обходить средства буферизации потока в целом(вполне), используя ввод и вывод низкого уровня, которые функционируют на описателях файла. См. Главу 8 [ввод - вывод низкого уровня].

Понятие Буферизации

Имеются три различных вида cтратегий буферизации:

Вновь открываемые потоки обычно полностью буферизируются, с одним исключением: поток, соединенный с интерактивным устройством типа терминала - изначально буферизирован строчно. См. Раздел 7.17.3 [Управление Буферизацией], для уточнения информации о том, как в выбирают различные виды буферизации.

Использование строчной буферизации для интерактивных устройств подразумевает окончание вывода сообщения с символом перевода строки. Вывод, который не заканчивается на символе перевода строки, может и не быть обнаружен немедленно, так если Вы хотите вывести его немедленно, Вы должны очистить буферизированный вывод функцией fflush, как описано в Разделе 7.17.2 [Очистка буфера].

Буферизация строки - хорошее значение по умолчанию для ввода терминала, т. к. большинство интерактивных программ читают команды, которые являются обычно одиночными строками. Программа должна быть способна выполнять каждую строку сразу же. Буферизированный ввод также согласуется с обычными редактирующими вводом средствами большинства операционных систем, которые работают внутри строки ввода.

Они включают программы, которые читают одиночно - символьные команды (подобно Emacs) и программам, которые делают их собственное редактирование ввода (типа тех что используют readline). Чтобы просто читать символ, не достаточно выключить буферизацию во входном потоке; Вы должны также выключить редактирование ввода, в операционной системе. Это требует изменения режима терминала (см. Раздел 12.4 [Режимы Терминала] ).

Промывание Буфера

Сброс вывода на буферизированном потоке, передает все накопленные символы в файл. Имеются много обстоятельств когда буферизированный вывод на потоке, сбрасывается автоматически:

Если Вы хотите сбросить буферизированный вывод в другой момент, вызывайте fflush, которая объявлен в заглавном файле ' stdio.h '.

-Функция: int fflush (FILE *stream)

Эта функция заставляет любой буферизированный вывод на потоке дописываться в файл. Если поток - нулевой указатель, то буферизированный вывод на всех открытых выходных потоках будет сброшен.

Эти функции возвращают EOF, если происходит ошибка записи, и нуль в другом случае.

Примечание о Совместимости: Некоторые поврежденные в уме операционные системы, как известно, были настолько помешаны на строчно ориентированном вводе, что для сброса строчно буферизированного потока должен быть введен символ перевода строки! К счастью, эта 'удобство', кажется, становится менее распространенным. В системе GNU беспокоиться об этом Вам нет нужды.

Управление Видом Буферизации

После открытия потока (но прежде любой другой операции на нем), Вы можете явно определить какую буферизацию Вы хотите, используя функцию setvbuf.

Средства, перечисленные в этом разделе объявлены в файле 'stdio.h'.

-Функция: int setvbuf (FILE *stream, char *buf, int mode, size_t size)

Эта функция используется, чтобы определить, что указанный поток должен иметь заданный режим буферизации, который может быть: _IOFBF (для полной буферизации), _IOLBF (для буферизации строки), или _IONBF (для небуферизованного ввода -вывода).

Если Вы определяете нулевой указатель как параметр buf, то setvbuf, распределяет буфер, непосредственно используя malloc. Этот буфер будет освобожден, когда Вы закроете поток.

Иначе, buf должен быть символьным массивом, который может содержать по крайней мере size символов. Вы не должны трогать пространство для этого массива, пока поток остается открытым и этот массив остается буфером. Использование автоматического массива - не очень хорошая идея, если Вы не закрываете файл перед выходом из блока, который объявляет массив.

В то время как массив остается буфером потоков, функции ввода ­ вывода потока используют буфер для их внутренних целей. Вы не должны пробовать обращаться к значениям в массиве непосредственно, в то время как поток использует его для буферизации.

Функция setvbuf возвращает ноль в случае успеха, или значение отличное от нуля, если значение режима не допустимо или если запрос не мог быть удовлетворен.

-Макрос: int _IOFBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть полностью буферизирован.

-Макрос: int _IOLBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть буферизирован строчно.

-Макрос: int _IONBF

Значение этой макрокоманды - константа integer, которая может использоваться как параметр режима для функции setvbuf, чтобы определить, что поток должен быть небуферизован.

-Макрос: int BUFSIZ

Значение этой макрокоманды - константа integer, которую удобно использовать как параметр size для setvbuf. Это значение, как гарантируют, будет по крайней мере 256.

Значение BUFSIZ выбрано в каждой системе таким, чтобы делать ввод - вывод потока наиболее эффективным.

Фактически, Вы можете лучшее значение, чтобы использовать для размера буфера посредством fstat системного вызова: его можно найти в st_blksize поле атрибутов файла. См. Раздел 9.8.1 [Значения Атрибута].

Иногда также используют BUFSIZ как размер распределения буферов, используемых для соответствующих целей, типа строк, используемых, чтобы получить строку ввода через fgets (см. Раздел 7.6 [Символьный Ввод]). Не имеется никакой специфической причины использовать BUFSIZ для этого вместо любого другого integer, за исключением того, что это могло бы привести к выполнению ввода - вывода в кусках эффективного размера.

-Функция: void setbuf (FILE *stream, char *buf )

Если buf - нулевой указатель, эффект этой функции эквивалентен вызову setvbuf с параметром режима _IONBF. Иначе, это эквивалентно вызову setvbuf с buf, и режимом _IOFBF и параметром size - BUFSIZ. Функция setbuf предусмотрена для совместимости со старым кодом; используйте setvbuf во всех новых программах.

-Функция: void setbuffer (FILE *stream, char *buf, size_t size)

Если buf - нулевой указатель, эта функция делает поток небуферизованным. Иначе, это делает поток полностью буферизированным с использованием buf в качестве буфера. Параметр size определяет длину buf.

Эта функция предусмотрена для совместимости со старым BSD кодом. Используйте вместо нее setvbuf.

-Функция: void setlinebuf (FILE *stream)

Эта функция делает поток буферизированным строчно, и распределяет буфер для Вас. Эта функция предусмотрена совместимость со старым BSD кодом. Используйте setvbuf вместо нее.

7.17 Другие Виды Потоков

Библиотека GNU обеспечивает способы определить дополнительные виды потоков, которые не обязательно соответствуют открытому файлу.

Один такой тип потока берет ввод из или пишет вывод в строку. Эти виды потоков используются внутренне, чтобы выполнить функции sprintf и sscanf. Вы можете также создавать такой поток явно, при использовании функций, описанных в Разделе 7.18.1 [Строковые Потоки].

В более общем смысле, Вы можете определять потоки, которые делают ввод -вывод для произвольных объектов, используя функции, обеспеченные вашей программой. Этот протокол обсужден в Разделе 7.18.3 [Заказные Потоки] .

Примечание о Переносимости: средства, описанные в этом разделе специфические для GNU. Другие системы или реализации C могли и не обеспечивать эквивалентные функциональные возможности.

Строковые Потоки

Функции fmemopen и open_memstream делали ввод - вывод в буфер памяти или строку. Эти средства объявлены в 'stdio.h '.

-Функция: FILE * fmemopen (void *buf, size_t size, const char *opentype)

Эта функция открывает поток, который допускает доступ, определенный параметром opentype, и который считывается или записывается в буфер, определенный параметром buf. Этот массив должен быть по крайней мере size байтов длиной.

Если Вы определяете нулевой указатель как параметр buf, fmemopen динамически распределяет (как с malloc; см. Раздел 3.3 [Беспрепятственное Распределение]) size байтовый массив. Это действительно полезно только, если Вы собираетесь записывать что-то в буфер и затем читать это обратно, т. к. Вы не имеете никакого способа фактически получить указатель на буфер (для этого, попробуете open_memstream, ниже). Буфер освобождается, когда открывается поток. Параметр opentype - такой же как в fopen (См. Раздел 7.3 [Открытие Потоков]). Если opentype определяет режим конкатенирования, то начальная файловая позиция устанавливается на первый символ пробела в буфере. Иначе начальная файловая позиция - в начале буфера.

Для потока, открытого для чтения, пустые символы (нулевые байты) в буфере не считаются концом файла. Операции чтения возвращают конец файла только когда файловая позиция продвигается за size байт. Так, если Вы хотите читать символы из строки с нулевым символом в конце, Вы должны обеспечить длину строки как аргумент size.

Вот пример использования fmemopen для создания потока для чтения из строки:

#include <stdio.h> static char buffer[] = 'foobar'; int main (void) { int ch; FILE *stream; stream = fmemopen (buffer, strlen (buffer), 'r'); while ((ch = fgetc (stream)) != EOF) printf ('Got %c\n', ch); fclose (stream); return 0; }

Эта программа производит следующий вывод:

Got f Got o Got o Got b Got a Got r
-Функция: FILE * open_memstream (char **ptr, size_t *sizeloc)

Эта функция открывает поток для записи в буфер. Буфер размещен динамически (как с malloc; см. Раздел 3.3 [Беспрепятственное Резервирование]) и растет по мере необходимости.

Когда поток закрывается с помощью fclose или сбрасывается с помощью fflush, указатели ptr и sizeloc модифицируются, и содержат указатель на буфер и size. Значения, таким образом сохраненные остаются допустимыми только, пока не происходит никакой дальнейший вывод на потоке. Если, Вы выводите еще, Вы должны промыть поток, чтобы сохранить новые значения прежде, чем Вы используете его снова.

Пустой символ записывается в конце буфера. Этот пустой символ не включен в значение size, сохраненное в sizeloc.

Вы можете перемещать файловую позицию потока функцией fseek (см. Раздел 7.15 [Позиционирование Файла]). Перемещение файловой позиции после конца уже записанных данных, заполняет захваченное пространство нулями.

Вот пример использования open_memstream:

#include <stdio.h> int main (void) { char *bp; size_t size; FILE *stream; stream = open_memstream (&bp, &size); fprintf (stream, 'hello'); fflush (stream); printf ('buf = `%s', size = %d\n', bp, size); fprintf (stream, ', world'); fclose (stream); printf ('buf = `%s', size = %d\n', bp, size); return 0; } Эта программа производит следующий вывод: buf = `hello', size = 5 buf = `hello, world', size = 12

Obstack Потоки

Вы можете открывать выходной поток, который помещает данные в obstack. См. Раздел 3.4 [Obstack].

-Функция: FILE * open_obstack_stream (struct obstack *obstack)

Эта функция открывает поток для записи данных в obstack. Она начинает объект в obstack и увеличивает его, при записывании данных (см. Раздел 3.4.6 [Возрастастающие Объекты]).

Вызов fflush на этом потоке модифицирует текущий размер объекта, в соответствии с количеством данных, которое было записано. После обращения к fflush, Вы можете исследовать объект.

Вы можете перемещать файловую позицию obstack потока функцией fseek (см. Раздел 7.15 [Позиционирование Файла]).

Чтобы сделать объект постоянным, модифицируйте obstack с fflush, и тогда используйте obstack_finish для завершения объекта и получения адреса. Следующая запись в поток, начинает новый объект в obstack.

Но как Вы узнаете, какой длины объект? Вы можете получать длину в байтах, вызывая obstack_object_size (см. Раздел 3.4.8 [Состояние Obstack]), или Вы можете пустым символом завершить объект, примерно так:

obstack_1grow (obstack, 0);

Вот типичная функция, которая использует open_obstack_stream:

char * make_message_string (const char *a, int b) { FILE *stream = open_obstack_stream (&message_obstack); output_task (stream); fprintf (stream, ': '); fprintf (stream, a, b); fprintf (stream, '\n'); fclose (stream); obstack_1grow (&message_obstack, 0); return obstack_finish (&message_obstack); }

Программирование Ваших Собственных Потоков

Этот раздел описывает, как Вы можете создавать потоки, которые берут ввод из произвольного источника данных или пишут вывод в произвольный сток данных, программируемый Вами. Назовем эти потоки пользовательскими.

Пользовательские Потоки и Cookies

Внутри каждого пользовательского потока есть специальный объект называемый cookie. Это - объект, определяемый Вами, который указывает, где берутся или сохраняются данные.

Чтобы реализовать пользовательский поток, Вы должны указать, как выбирать или сохранять данные в заданном месте. Это делается определением функции ловушки для чтения, записи, изменения 'файловой позиции', и закрытия потока. Вся четыре из этих функций будут переданы cookie потока, так чтоони могут сообщать, где брать или сохранять данные. Библиотечные функции не знают что находится внутри cookie, но ваши функции будут знать.

Когда Вы создаете пользовательский поток, Вы должны задать указатель на cookie, а также четыре функции ловушки, содержащиеся в структуре типа struct cookie_io_functions.

Эти средства объявлены в ' stdio.h '.

-Тип даннаых: struct cookie_io_functions

Это - тип структуры, который содержит функции, которые определяют протокол связи между потоком и cookie. Он имеет следующие элементы:

cookie_read_function *read

Это функция, которая читает данные из cookie. Если значение является пустым указателем вместо функции, то операции чтения на потоке всегда возвращает EOF.

cookie_write_function *write

Это - функция, которая пишет данные в cookie. Если значение является пустым указателем вместо функции, то данные, записанные в поток отбрасываются.

cookie_seek_function *seek

Это функция, которая выполняет эквивалент позиционирования файла на cookie. Если значение является пустым указателем вместо функции, обращения к fseek на этом потоке, может искать расположения только внутри буфера; любая попытка искать снаружи буфера, возвратит ESPIPE ошибку.

cookie_close_function *close

Эта функция выполняет любую соответствующую сброс cookie при закрытии потока. Если значение является пустым указателем вместо функции, то при закрытии потока ничего не делается для закрытия cookie.

-Функция: FILE * fopencookie (void *cookie, const char *opentype, struct

Эта функция фактически создает поток для сообщения с cookie используя функции в io_functions аргументе. Opentype аргумент интерпретируется как для fopen; см. Раздел 7.3 [Открытие Потоков]. (Но отметьте что опция 'усечения при открытии' игнорируется.) Новый поток полностью буферизирован.

Функция fopencookie возвращает недавно созданный поток, или пустой указатель в случае ошибки.

Пользовательские Функции-Ловушки Потока

Имеется большое количество деталей определения четырех функций ловушки, которые необходимы для пользовательского потока.

Вы должны определить функцию для чтения данных из cookie как:

ssize_t reader (void *cookie, void *buffer, size_t size)
Это очень похоже на функцию read; см. Раздел 8.2 [Примитивы ввода - вывода]. Ваша функция должна передать до size байтов в буфер, и возвращать число прочитанных байтов, или нуль указывая конец файла. Вы можете возвращать значение -1 для указания ошибки.

Вы должны определить функцию, для записи данных в cookie как:

ssize_t writer (void *cookie, const void *buffer, size_t size)
Это очень похоже на функцию write; см. Раздел 8.2 [Примитивы ввода - вывода]. Ваша функция должна передать до size байтов из буфера, и возвращать число записанных байтов.

Вы должны определить функцию, для выполнения операции позиционирования на cookie как:

int seeker (void *cookie, fpos_t *position, int whence)

Для этой функции, position и whence аргументы интерпретируются как для fgetpos; см. Раздел 7.16 [Переносимое Позиционирование]. В библиотеке GNU, fpos_t эквивалентен off_t или long int, и просто представляет число байтов от начала файла.

После выполнения операции установки, ваша функция должна сохранить возникающую в результате файловую позицию относительно начала файла в position. Ваша функция должна возвратить значение 0 в случае успеха, и -1 в случае ошибки.

Вы должны определить функцию, для операции очистки на cookie, при закрытии потока как:

int cleaner (void *cookie)
Ваша функция должна возвратить -1, указывая ошибку, или 0 иначе.
-Тип данных: cookie_read_function

Это - тип данных, который должна иметь функция чтения для пользовательского потока. Если Вы объявляете функцию как показано выше, это тип, который она будет иметь.

-Тип данных: cookie_write_function

Тип данных пишущей функции для пользовательского потока.

-Тип данных: cookie_seek_function

Тип данных функции инициализации для пользовательского потока.

-Тип данных: cookie_close_function

Тип данных функции close для заказного потока.


Вперед Назад Содержание