Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шилдт c++_базовый_курс издание 3.pdf
Скачиваний:
3062
Добавлен:
27.03.2016
Размер:
9.82 Mб
Скачать

Приложение А: С-ориентированная система ввода-вывода

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

Узелок на память. Для С++-программ необходимо использовать объектноориентированную С++-систему ввода-вывода.

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

Система ввода-вывода языка С требует включать в программы заголовочный файл stdio.h (ему соответствует заголовок <cstdio>, отвечающий новому стилю). Каждая С-программа должна использовать заголовочный файл stdio.h, поскольку язык С не поддерживает С++- стиль включения заголовков. С++-программа может работать с использованием любого из этих двух вариантов. Заголовок <cstdio> помещает свое содержимое в пространство имен std, а заголовочный файл stdio.h— в глобальное пространство имен, что соответствует С- ориентации. В этом приложении в качестве примеров приведены С-программы, поэтому они используют С-стиль включения заголовочного файла stdio.h и не требуют установки пространства имен.

И еще. Как отмечалось в главе 1, стандарт языка С был обновлен в 1999 году и получил название стандарта С99. В то время в С-систему ввода-вывода было внесено несколько усовершенствований. Но поскольку C++ опирается на стандарт С89, то он не поддерживает средств, которые были добавлены в стандарт С99. (Более того, на момент написания этой книги ни один из широко доступных компиляторов C++ не поддерживал стандарт С99. Да и ни одна из широко распространяемых программ не использовала средства стандарта С99.) Поэтому здесь не описываются средства, внесенные в С-систему ввода-вывода стандартом С99. Если же вас интересует язык С, включая полное описание его системы ввода-вывода и средств, добавленных стандартом С99, я рекомендую обратиться к моей книге Полный справочник по С, М.: Издательский дом "Вильямс".

Использование потоков в С-системе ввода-вывода

Подобно С++-системе ввода-вывода, С-ориентированная система ввода-вывода опирается на понятие потока. В начале работы программы автоматически открываются три

заранее определенных текстовых потока: stdin, stdout и stderr. Их называют стандартными

потоками ввода данных (входной поток), вывода данных (выходной поток) и ошибок

соответственно. (Некоторые компиляторы открывают также и другие потоки, которые зависят от конкретной реализации системы.) Эти потоки представляют собой С-версии потоков cin, cout и cerr соответственно. По умолчанию они связаны с соответствующим системным устройством.

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

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

Функции printf() и scanf()

Двумя самыми популярными С-функциями ввода-вывода являются printf() и scanf(). Функция printf() записывает данные в стандартное устройство вывода (консоль), а функция scanf(), ее дополнение, считывает данные с клавиатуры. Поскольку язык С не поддерживает перегрузку операторов и не использует операторы "<<" и ">>" в качестве операторов ввода-вывода, то для консольного ввода-вывода используются именно функции printf() и scanf(). Обе они могут обрабатывать данные любых встроенных типов, включая символы, строки и числа. Но поскольку эти функции не являются объектно-ориентированными, их нельзя использовать непосредственно для ввода-вывода объектов классов, создаваемых программистом.

Функция printf()

Функция printf() имеет такой прототип:

int printf(const char *fmt_string, ...);

Первый аргумент, fmt_string, определяет способ отображения всех последующих аргументов. Этот аргумент часто называют строкой форматирования. Она состоит из элементов двух типов: текста и спецификаторов формата. К элементам первого типа относятся символы (текст), которые выводятся на экран. Элементы второго типа (спецификаторы формата) содержат команды форматирования, которые определяют способ отображения аргументов. Команда форматирования начинается с символа процента, за которым следует код формата. Спецификаторы формата перечислены в табл. А.1. Количество аргументов должно в точности совпадать с количеством команд форматирования, причем совпадение обязательно и в порядке их следования. Например, при

вызове следующей функции printf().

printf ("Привет %с %d %s", 'с', 10, "всем!");

На экране будет отображено: "Привет с 10 всем! ".

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

Команды формата могут иметь модификаторы, которые задают ширину поля, точность (количество десятичных разрядов) и признак выравнивания по левому краю. Целое значение, расположенное между знаком % и командой форматирования, выполняет роль спецификатора минимальной ширины поля. Наличие этого спецификатора приведет к тому, что результат будет заполнен пробелами или нулями, чтобы гарантированно обеспечить для выводимого значения заданную минимальную длину. Если выводимое значение (строка или число) больше этого минимума, оно будет выведено полностью, несмотря на превышение минимума. По умолчанию в качестве заполнителя используется пробел. Для заполнения нулями нужно поместить 0 перед спецификатором ширины поля. Например, строка форматирования %05d дополнит выводимое число нулями (их будет меньше пяти), чтобы общая длина была равной пяти символам.

Точное значение модификатора точности зависит от кода формата, к которому он применяется. Чтобы добавить модификатор точности, поставьте за спецификатором ширины поля десятичную точку, а после нее — значение спецификации точности. Для форматов а, А, е, Е, f и F модификатор точности определяет число выводимых десятичных знаков. Например, строка форматирования %10.4f обеспечит вывод числа, ширина которого составит не меньше десяти символов, с четырьмя десятичными знаками. Применительно к целым или строкам, число, следующее за точкой, задает максимальную длину поля. Например, строка форматирования %5.7s отобразит строку длиной не менее пяти, но не более семи символов. Если выводимая строка окажется длиннее максимальной длины поля, конечные символы будут отсечены.

По умолчанию все выводимые значения выравниваются по правому краю: если ширина поля больше выводимого значения, оно будет выровнено по правому краю поля. Чтобы установить выравнивание по левому краю, поставьте знак "минус" сразу после знака %. Например, строка форматирования %-10.2f обеспечит выравнивание вещественного числа (с двумя десятичными знаками в 10-символьном поле) по левому краю. Рассмотрим программу, в которой демонстрируется использование спецификаторов ширины поля и выравнивания по левому краю.

#include <stdio.h>

int main()

{

printf("|%11.6f|\n", 123.23);

printf ("|%-11.6f|\n", 123.23);

printf("|%11.6s |\n", "Привет всем");

printf("|%-11.6s |\n", "Привет всем");

return 0;

}

При выполнении эта программа отображает такие результаты.

| 123.230000 |

| 123.230000 |

| Привет|

| Привет |

Существуют два модификатора команд форматирования, которые позволяют функции printf() отображать короткие (short) и длинные (long) целые. Эти модификаторы могут применяться к спецификаторам типа d, i, о, и, х и X. Модификатор l уведомляет функцию printf() о длинном формате значения. Например, строка %ld означает, что должно быть выведено длинное целое. Модификатор h указывает на применение короткого формата. Следовательно, строка %hu означает, что выводимое целочисленное значение имеет тип short unsigned.

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

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

Модификатор l можно также поставить перед командами форматирования вещественных чисел е, Е, f, F, g и G. В этом случае он уведомит о выводе значения типа long double.

Функция scanf()

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

int scanf (const char *fmt_string, ...);

Управляющая строка, задаваемая параметром fmt_string, состоит из символов трех категорий:

■ спецификаторов формата; ■"пробельных" символов (пробелы, символы табуляции и пустой строки); ■ символов, отличных от "пробельных".

Функция scanf() возвращает количество введенных полей, а при возникновении ошибки

— значение EOF (оно определено в заголовке stdio.h).

Спецификаторы формата — им предшествует знак процента (%) — сообщают, какого типа данное будет считано следующим. Например, спецификатор %s прочитает строку, а %d — целое значение. Эти коды приведены в табл. А.2.

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

"Непробельный" символ в строке форматирования заставляет функцию scanf() прочитать и отбросить соответствующий символ. Например, при использовании строки форматирования %d, %d функция scanf() сначала прочитает целое значение, затем прочитает и отбросит запятую и наконец прочитает еще одно целое. Если заданный символ не обнаружится, работа функции scanf() будет завершена.

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

scanf("%d", &count);

Строки обычно считываются в символьные массивы, а имя массива (без индекса) является адресом первого элемента в этом массиве. Поэтому, чтобы считать строку в символьный массив address, используйте такой код.

char address[80];

scanf("%s", address);

В этом случае параметр address уже является указателем, и поэтому его не нужно предварять оператором "&".

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

scanf("%d%d", &r, &с);

Примет значения, введенные как 10 20, но наотрез откажется от "блюда", поданного в виде 10,20.

Подобно printf(), в функции scanf() спецификаторы формата по порядку сопоставляются с переменными, перечисленными в списке аргументов.

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

scanf("%d%*c%d", &х, &у);

при вводе данных в виде 10/20 поместит значение 10 в переменную х, отбросит знак деления и присвоит значение 20 переменной у.

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

scanf ("%20s", str);

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

ABCDEFGHIJKLMNOPQRSTUVWXYZ,

то в переменную str будут приняты только первые 20 символов (до буквы 'Т'), поскольку команда форматирования здесь содержит модификатор максимальной длины поля.

Это означает, что остальные символы, "UVWXYZ", не будут использованы вообще. В случае другого вызова функции scanf().

scanf("%s", str);

символы "UVWXYZ" поместились бы в переменной str. При обнаружении "пробельного"

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

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

scanf ("%с%с%с", &а, &b, &с);

поместит символ х в переменную а, пробел — в переменную b и символ у — в переменную с.

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

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

%[XYZ]

Соответствующая набору переменная должна быть указателем на массив символов. При возврате из функции scanf() этот массив будет содержать строку с завершающим нулем, состоящую из считанных символов. Например, следующая программа использует набор сканируемых символов для считывания цифр в массив s1. Если будет введен символ, отличный от цифры, массив s1 завершится нулевым символом, а остальные символы будут считываться в массив s2 до тех пор, пока не будет введен следующий "пробельный" символ.

/* Простой пример использования набора сканируемых символов.

*/

#include <stdio.h>

int main()

{

char s1 [80], s2 [80];

printf("Введите числа, а затем несколько букв:\n");

scanf("%[0123456789]%s", s1, s2);

printf("%s %s", s1, s2);

return 0;

}

Многие компиляторы позволяют с помощью дефиса задать в наборе сканируемых символов диапазон. Например, при выполнении следующей инструкции функция scanf() будет принимать символы от А до Z.

% [А-Z]

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

/* Пример использования в наборе сканируемых символов нескольких диапазонов.

*/

#include <stdio.h>

int main()

{

char s1[80], s2 [80];

printf("Введите числа, а затем несколько букв:\n");

scanf("%[0-9]%[a-zA-Z]", s1, s2);

printf ("%s %s", s1, s2);

return 0;

}

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

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

/* Пример использования набора сканируемых символов для запрещения считывания указанных в нем символов.

*/

#include <stdio.h>

int main()

{

char s1[80], s2[80];

printf("Введите не цифры, а затем не буквы:\n");

scanf("%[^0-9]%[^a-zA-Z]", s1, s2);

printf ("%s %s", s1, s2);

return 0;

}

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

Некоторые спецификаторы формата могут использовать такие модификаторы, которые точно указывают тип переменной, принимающей данные. Чтобы прочитать длинное целое, поставьте перед спецификатором формата модификатор l, а чтобы прочитать короткое целое — модификатор h. Эти модификаторы можно использовать с кодами формата d, i, o, u, x и n.

По умолчанию спецификаторы f, е и g заставляют функцию scanf() присваивать данные переменным типа float. Если поставить перед одним из этих спецификаторов формата модификатор l, функция scanf() присвоит прочитанное данное переменной типа double. Использование же модификатора L означает, что переменная, принимающая значение,

имеет тип long double.

Модификатор l можно применить и к спецификатору с, чтобы обозначить указатель на двухбайтовый символ с типом данных wchar_t (если ваш компилятор соответствует

стандарту C++). Модификатор l можно также использовать с кодом формата s, чтобы обозначить указатель на строку двухбайтовых символов. Кроме того, модификатор l можно использовать для модификации набора сканируемых двухбайтовых символов.

С-система обработки файлов

Несмотря на то что файловая система в С отличается от той, что используется в C++, между ними есть много общего. С-система обработки файлов состоит из нескольких взаимосвязанных функций. Наиболее популярные из них перечислены в табл. А.З.

Общий поток, который "цементирует" С-систему ввода-вывода, представляет собой файловый указатель (file pointer). Файловый указатель — это указатель на информацию о файле, которая включает его имя, статус и текущую позицию. По сути, файловый указатель идентифицирует конкретный дисковый файл и используется потоком, чтобы сообщить всем С-функциям ввода-вывода, где они должны выполнять операции. Файловый указатель — это переменная-указатель типа FILE, который определен в заголовке stdio.h.

Функция fopen()

Функция fopen() выполняет три задачи.

1.Открывает поток.

2.Связывает файл с потоком.

3.Возвращает указатель типа FILE на этот поток.

Чаще всего под файлом подразумевается дисковый файл. Функция fopen() имеет такой прототип.

FILE *fopen(const char *filename, const char *mode);

Здесь параметр filename указывает на имя открываемого файла, а параметр mode— на строку, содержащую нужный статус (режим) открытия файла. Возможные значения параметра mode показаны в приведенной табл. А.4. Параметр filename должен представлять строку символов, составляющих имя файла, которое допустимо в данной операционной системе. Эта строка может включать спецификацию пути, если действующая среда поддерживает такую возможность.

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

Как видно из табл. А.4, файл можно открывать либо в текстовом, либо в двоичном режиме. При открытии в текстовом режиме выполняются преобразования некоторых последовательностей символов. Например, символы новой строки преобразуются в последовательности символов "возврат каретки"/"перевод строки". В двоичном режиме подобные преобразования не выполняются.

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

fp = fopen("test", "w");

Здесь переменная fp имеет тип FILE*. Однако зачастую для открытия файла используется такой код.

if((fp = fopen ("test", "w"))==NULL) {

printf ("Не удается открыть файл.");

exit(1);

}

При таком методе выявляется любая ошибка, связанная с открытием файла (например, при использовании защищенного от записи или заполненного диска), и только после этого можно предпринимать попытку записи в заданный файл. NULL — это макроимя, определенное в заголовке stdio.h.

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

Функция fputc()

Функция fputc() используется для вывода символов в поток, предварительно открытый для записи с помощью функции fopen(). Ее прототип имеет следующий вид.

int fputc(int ch, FILE *fp);

Здесь параметр fp — файловый указатель, возвращаемый функцией fopen(), а параметр ch — выводимый символ. Файловый указатель сообщает функции fputc(), в какой дисковый файл необходимо записать символ. Несмотря на то что параметр ch имеет тип int, в нем используется только младший байт.

При успешном выполнении операции вывода функция fputc() возвращает записанный в файл символ, в противном случае — значение EOF.

Функция fgetc()

Функция fgetc() используется для считывания символов из потока, открытого в режиме чтения с помощью функции fopen(). Ее прототип имеет следующий вид.

int fgetc(FILE *fp);

Здесь параметр fp — файловый указатель, возвращаемый функцией fopen(). Несмотря на то что функция fgetс() возвращает значение типа int, его старший байт равен нулю.

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

ch = fgetc(fp);

while(ch!=EOF) {

ch = fgetc(fp);

}

Функция feof()

Файловая система в С может также обрабатывать двоичные данные. Если файл открыт в режиме ввода двоичных данных, то не исключено, что может быть считано целое число, равное значению EOF. В этом случае при использовании такого кода проверки достижения конца файла, как ch != EOF, будет создана ситуация, эквивалентная получению сигнала о достижении конца файла, хотя в действительности физический конец файла может быть еще не достигнут. Чтобы решить эту проблему, в языке С предусмотрена функция feof(), которая используется для определения факта достижения конца файла при считывании двоичных данных. Ее прототип имеет такой вид.

int feof(FILE *fp);

Здесь параметр fp идентифицирует файл. Функция feof() возвращает ненулевое значение, если конец файла был-таки достигнут; в противном случае — нуль. Таким образом, при выполнении следующей инструкции будет считано все содержимое двоичного файла.

while(!feof(fp)) ch = fgetc(fp);

Безусловно, этот метод применим и к текстовым файлам.

Функция fclose()

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

Функция fclose() имеет следующий прототип,

int fclose(FILE *fp);

Здесь параметр fp — файловый указатель, возвращаемый функцией fopen(). При успешном выполнении функция fclose() возвращает нуль; в противном случае возвращается значение EOF. Попытка закрыть уже закрытый файл расценивается как ошибка. При удалении носителя данных до закрытия файла будет сгенерирована ошибка, как и в случае недостатка свободного пространства на диске.

Использование функций fopen(), fgetc(), fputc() и fclose()

Функции fopen(), fgetc(), fputc() и fclose() составляют минимальный набор операций с файлами. Их использование демонстрируется в следующей программе, которая выполняет копирование файла. Обратите внимание на то, что файлы открываются в двоичном режиме и что для проверки достижения конца файла используется функция feof().

/* Эта программа копирует содержимое одного файла в другой.

*/

#include <stdio.h>

int main(int argc, char *argv[])

{

FILE *in, *out;

char ch;

if(argc!=3) {

printf("Вы забыли ввести имя файла.\n");

return 1;

}

if((in=fopen(argv[1], "rb")) == NULL) {

printf("He удается открыть исходный файл.\n");

return 1;

}

if((out=fopen(argv[2], "wb")) == NULL) {

printf("He удается открыть файл-приемник.\n");

return 1;

}

/* Код копирования содержимого файла. */

while(!feof (in)) {

ch = fgetc(in);

if(!feof(in)) fputc (ch, out);

}

fclose(in);

fclose(out);

return 0;

}

Функции ferror() и rewind()

Функция ferror() используется для определения факта возникновения ошибки при выполнении операции с файлом. Ее прототип имеет такой вид.

int ferror(FILE *fp);

Здесь параметр fp — действительный файловый указатель. Функция ferror() возвращает значение true, если при выполнении последней файловой операции произошла ошибка; в противном случае — значение false. Поскольку возникновение ошибки возможно при выполнении любой операции с файлом, функцию ferror() необходимо вызывать сразу после каждой функции обработки файлов; в противном случае информацию об ошибке можно попросту потерять.

Функция rewind() перемещает индикатор позиции файла в начало файла, заданного в качестве аргумента. Ее прототип выглядит так.

void rewind(FILE *fp);

Здесь параметр fp — действительный файловый указатель.

Функции fread() и fwrite()

В файловой системе языка С предусмотрено две функции, fread() и fwrite(), которые позволяют считывать и записывать блоки данных. Эти функции подобны С++-функциям read() и write(). Их прототипы имеют следующий вид.

size_t fread(void *buffer, size_t num_bytes, size_t count, FILE *fp);

size_t fwrite(const void *buffer, size_t num_bytes, size_t count, FILE *fp);

При вызове функции fread() параметр buffer представляет собой указатель на область памяти, которая предназначена для приема данных, считываемых из файла. Функция

считывает count объектов длиной num_bytes из потока, адресуемого файловым указателем fp. Функция fread() возвращает количество считанных объектов, которое может оказаться меньше заданного значения count, если при выполнении этой операции возникла ошибка или был достигнут конец файла.

При вызове функции fwrite() параметр buffer представляет собой указатель на информацию, которая подлежит записи в файл. Эта функция записывает count объектов длиной num_bytes в поток, адресуемый файловым указателем fp. Функция fwrite() возвращает количество записанных объектов, которое будет равно значению count, если при выполнении этой операции не было ошибки.

Если при вызове функций fread() и fwrite() файл был открыт для выполнения двоичной операции, то они могут считывать или записывать данные любого типа. Например, следующая программа записывает в дисковый файл значение типа float.

/* Запись в дисковый файл значения с плавающей точкой.

*/

#include <stdio.h>

int main()

{

FILE *fp;

float f = 12.23F;

if((fp=fopen("test", "wb"))==NULL) {

printf("He удается открыть файл.\n");

return 1;

}

fwrite(&f, sizeof(float), 1, fp);

fclose(fp);

return 0;

}

Как показано в этой программе, роль буфера может выполнять (и при том довольно часто) одна переменная.

С помощью функций fread() и fwrite() часто выполняется считывание и запись

содержимого массивов или структур. Например, следующая программа, используя одну только функцию fwrite(), записывает содержимое массива значений с плавающей точкой balance в файл с именем balance. Затем с помощью одной лишь функции fread() программа считывает элементы этого массива и отображает их на экране.

#include <stdio.h>

int main()

{

register int i;

FILE *fp;

float balance[100];

/* Открываем файл для записи. */

if((fp=fopen("balance", "w"))==NULL) {

printf("He удается открыть файл.\n");

return 1;

}

for(i=0; i<100; i++) balance[i] = (float) i;

/* Одним "махом" сохраняем весь массив balance. */

fwrite(balance, sizeof balance, 1, fp);

fclose(fp);

/* Обнуляем массив. */

for(i=0; i<100; i++) balance[i] = 0.0;

/* Открываем файл для чтения. */

if((fp=fopen("balance", "r"))==NULL) {

printf("He удается открыть файл.\n");

return 1;

}

/* Одним "махом" считываем весь массив balance. */

fread(balance, sizeof balance, 1, fp);

/* Отображаем содержимое массива. */

for(i=0; i<100; i++) printf("%f ", balance[i]);

fclose(fp);

return 0;

}

Использовать функции fread() и fwrite() для считывания и записи блоков данных более эффективно, чем многократно вызывать функции fgetc() и fputc().

Функция fseek() и выполнение ввода-вывода с произвольным доступом

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

int fseek(FILE *fp, long numbytes, int origin);

Здесь параметр fp — файловый указатель, возвращаемый функцией fopen(), параметр numbytes — количество байтов относительно исходного положения, заданного параметром origin. Параметр origin может принимать одно из следующих макроимен (определенных в заголовке stdio.h).

Следовательно, чтобы переместить индикатор позиции в файле на numbytes байтов относительно его начала, в качестве параметра origin необходимо использовать значение SEEK_SET. Для перемещения относительно текущей позиции используйте значение SEEK_CUR, а для смещения с конца файла — значение SEEK_END.

Нулевое значение результата функции свидетельствует об успешном выполнении функции fseek(), а ненулевое— о возникновении сбоя. Как правило, функцию fseek() не рекомендуется использовать для файлов, открытых в текстовом режиме, поскольку преобразование символов может привести к ошибочным перемещениям индикатора позиции в файле. Поэтому лучше использовать эту функцию для файлов, открытых в двоичном режиме. Например, если вам нужно считать 234-й байт в файле test, выполните следующий код.

int func1()

{

FILE *fp;

if((fp=fopen("test", "rb")) == NULL) {

printf("He удается открыть файл.\n");

exit (1);

}

fseek(fp, 234L, SEEK_SET);

return getc(fp); /* Считывание одного символа, расположенного на 234-й позиции. */

}

Функции fprintf() и fscanf()

Помимо рассмотренных выше основных функций ввода-вывода, С-система ввода-вывода включает функции fprintf() и fscanf(). Поведение этих функций аналогично поведению