Параметры функции:
stream Указатель на объект типа FILE, который связан с потоком (потоковая переменная).
format Строка форматов, содержащая текст, который будет выведен в поток. Опционально, строка может содержать встроенные метки форматирования (спецификации вывода), которые заменяются значениями, указанными в последующих дополнительных аргументах и отформатированы требуемым образом.
Форматируемые теги должны следовать следующему формату записи:
%[флаги][ширина поля][.точность][длина]спецификатор
Спецификаторы задают тип выводимых данных и способ из представления. Ниже приведены спецификаторы форматирования потоков ввода/вывода:
Таблица 1. Спецификаторы вывода функции fprintf
|
Спецификатор |
Описание |
|
c |
Символ |
|
d или i |
Знаковое десятичное число. |
|
e |
Экспоненциальная форма записи чисел (мантисса/экспонента) с использованием символаe. |
|
E |
Экспоненциальная форма записи чисел (мантисса/экспонента) с использованием символаE. |
|
f |
Десятичное значение с плавающей точкой. |
|
g |
Используется для обозначения короткого %e или %f |
|
G |
Используется для обозначения короткого %E или %f |
|
o |
Восьмеричная система счисления |
|
s |
Строка символов. |
|
u |
Использовать беззнаковое целое десятичное значение. |
|
x |
Использовать беззнаковые шестнадцатеричные значения |
|
X |
Шестнадцатеричные целые без знака (прописные буквы) |
|
p |
Указатель на адрес |
|
n |
Запись по указателю, переданному в качестве аргумента, количества символов, записанных на момент появления командной последовательности, содержащей n |
|
% |
Вывод символа % |
Форматирующие теги могут также содержать флаги, ширину, точность и модификаторы, которые являются необязательными:
Таблица 2. Флаги вывода функции fprintf
|
Флаг |
Описание |
|
- |
Выравнивание по левому краю выделенного поля. По умолчанию стоит правостороннее выравнивание в выделенном поле. |
|
+ |
Использовать знаки плюс или минус для вывода значений. По умолчанию выводится только знак минус, если значение отрицательное. |
|
(space) |
Если в строке есть символ пробела, он не игнорируется, а попадает в поток вывода. |
|
# |
Используется вместе со спецификаторами o, x или X специфицирующих значение отличное от нуля, 0x или0X. Используется вместе со спецификаторами e, E и f, принудительно выводит десятичную точку, даже, если у значения нет цифр в дробной части. По умолчанию, если нет цифр в дробной части, десятичная точка не отображается. Используется с a, A, e, E, f, F, g или G результат такой же, как и со спецификаторами е или Е, но нули не удаляются. |
|
0 |
Левая часть после числа заполняется нулями (0) вместо пробелов, если ширина поля больше числа. |
Таблица 3. Ширина вывода для функции fprintf
|
ширина |
Описание |
|
number |
Минимальное количество символов для печати. Если значение для печати короче, чем это число, пустые места заполняются пробелами. Значение не обрезается, даже если оно больше number. |
|
* |
Ширина не указана в строке формата, но в качестве дополнительного аргумента целое значение предшествующего тому аргументу, который должен быть отформатирован. |
Таблица 4. Точность вывода функции fprintf
|
.точность |
Описание |
|
.number |
Для спецификаторов целых чисел d, i, o, u, x, X: указывает минимальное количество цифр. Если значение, короче, чем это число, пустое место заполняется нулями. Значение не обрезается, даже если number меньше, чем длина выводимого объекта. Для спецификаторов е, Е и F: это количество цифр, которое будет напечатано после десятичной точки. Для спецификаторов g и G это максимальное количество значащих цифр для печати. Для спецификатора s - это максимальное количество символов для печати. Для c этот параметр не имеет значения. При отсутствии этого параметра, точность по умолчанию равна 1. |
|
.* |
Точность не указана в строке формата, но в качестве дополнительного аргумента передается целое значение, оно должно предшествовать форматируемому аргументу. |
Таблица 5. Длина вывода функции fprintf
|
длина |
Описание |
|
h |
short int или unsigned short int (применяется только к целым спецификаторам: i, d, o, u, x иX). |
|
l |
long int или unsigned long int в случае применения к целым спецификаторам (i, d, o, u, x и X), а также к широким символам или строкам с широкими символами, для спецификаторов c и s. |
|
L |
Long double в случае применения к спецификаторам вещественных типов %e, %E, %f, %F, %g, %G) |
В случае успешного вывода функция fprintf возвращает общее число записанных символов. В случае неудачи, возвращается отрицательное число.
Рассмотрим пример форматированного вывода информации в поток
//Листинг 2. Запись данных в поток функцией fprintf
#include <stdio.h>
#include <windows.h>
int main(int argc, _TCHAR* argv[])
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
int n, age;
char name[20];
FILE* pFile = fopen ("d:\\myfile.txt","w");
for (n=0 ; n<5 ; n++)
{
puts ("Введите имя: ");
gets (name);
puts ("Введите возраст: ");
scanf("%d", &age);
fflush(stdin);
fprintf (pFile, "%2d. Имя [%-20.20s] Возраст [%-3d]\n",n,name, age);
}
fclose (pFile);
return 0;
}
В приведенном примере для функции fprintf использованы три спецификатора вывода – для вывода порядкового номера %2d (целое число, под вывод отводится 2 позиции), имени %-20.20s (строка, выровненная по левому краю, под вывод отводится минимум 20 позиций, максимально выводится 20 символов) и возраст %-3d (целое число, под вывод отводится 3 позиции, вывод выравнивается по левой границе). В результате в файле получим следующее содержимое:
0. Имя [Иван Иванов ] Возраст [23 ]
1. Имя [Ольга Александрова ] Возраст [19 ]
2. Имя [Петр Петров ] Возраст [38 ]
3. Имя [Мария Сидорова ] Возраст [29 ]
4. Имя [Егор Егоров ] Возраст [46 ]
Для чтения форматированных данных из потока необходимо использовать функцию fscanf.
int fscanf (FILE * stream, char * format_string, …); где stream - потоковая переменная, format_string - строка управления форматом.
Функция fscanf считывает данные из текущей позиции потока stream в место, определяемое заданием аргументов, указанных в вызове после строки форматов. Каждый аргумент должен быть указателем на переменную типа, соответствующего типу, заданному в спецификации ввода строки формата.
Спецификация ввода имеет следующий формат:
%[*][ширина][длина]спецификатор
Спецификаторы для ввода совпадают с указанным в таблице 1. Например, чтобы считать из потока данные, записанные в примере из листинге 2, необходимо вызвать функцию fscanf следующим образом:
pFile = fopen ("d:\\myfile.txt","r");
while(!feof(pFile))
{ fscanf (pFile, "%2d. Имя [%20s ] Возраст [%d ]\n",&n, name, &age);
printf("Номер: %2d Имя %s Возраст %d\n", n, name, age);
}
Использование функции fscanf для чтения данных сложного формата может приводить к проблемам интрепретации считанных данных, поскольку в качестве разделителя считываемых объектов используются пробел и пробельные символы. Необходимо строго следить за соответствием формата записи (в fprintf) и считывания (в fscanf).
Ввод-вывод двоичных данных проще всего реализовать с использованием функций чтения-записи с использованием буфера fread и fwrite. Эти функции позволяют читать и записывать блоки данных любого типа. Рассмотрим прототипы этих функций:
size_t fread (void * buffer, size_t size,
size_t count, FILE * fp);
size_t fwrite (const void * buffer, size_t size,
size_t count, FILE * fp);
buffer– указатель на область памяти, в которую будут прочитаны данные из потока (для fread) или из которой будут выводиться данные в поток (для fwrite).
count — счетчик, определяющий, сколько считывается и записывается блоков данных,
size - задает размера блока считываемого/записываемого блока данных.
fp – потоковая переменная, связанная с потоком, из которого/в который читаются/пишутся данные.
Функция fread возвращает количество прочитанных блоков. Если прочитать данные в запрошенном объеме не удалось ( достигнут конец файла или произошла ошибка), то функция может вернуть значение, меньшее, чем значение счетчика. Аналогично, функция fwrite возвращает количество записанных блоков данных и возвращаемый результат будет равен значению счетчика при вызове, если не произошло ошибок. Функции fread() и fwrite() можно адаптировать для записи объектов различных типов данных. Можно записать в файл значение переменной:
|
Вывод с использованием fwrite |
Форматированный вывод fprintf |
|
int x=142822; fwrite(&x, 1, sizeof(int), fp); |
double x=142822; fprintf(fp, “%d”, x); |
|
Содержимое файла после вывода: двоичное Значение переменной займет в файле 4 байта (по размеру переменной) |
Содержимое файла после вывода: тестовое Значение переменной займет в файле 6 байта (по количеству цифр) |
Если необходимо вывести в файл содержимое массива, функция fwrite позволяет обойтись без использования цикла:
|
Вывод с использованием fwrite |
Форматированный вывод fprintf |
|
const int N=10; double mas[N]; …//инициализация массива fwrite(mas, N, sizeof(double), fp); |
const int N=10; double mas[N]; …//инициализация массива for(int i=0; i<N; i++) fprintf(fp, “mas[%d]=%f”, i,mas[i]); |
При выводе (вводе) структурной переменной нет необходимости отдельно записывать (считывать) каждое поле:
|
Вывод с использованием fwrite |
Форматированный вывод fprintf |
|
struct MyStruct { int field1; char field2[40]; double field[3]; } m; fread(&m, 1, sizeof(MyStruct), fp); |
struct MyStruct { int field1; char field2[40]; double field[3]; } m; fscanf(fp, “%d”, &m.field1); fscanf(fp, “%s”, m.field2); for(int i=0; i<3; i++) fscanf(fp, “%f”, &m.field3[i]); |
Еще одно достоинство fwrite/fread перед fscanf/fprintf – в скорости выполнения операций. Функции форматированного ввода-вывода перед непосредственно перемещением данных форматируют в соответствии с заданной стройкой форматов, а это требует дополнительного времени. При использовании fwrite/fread форматирования данных не производится, операции выполняются быстрее.
Язык С++ также поддерживает концепцию объектно-ориентированного ввода-вывода, но потоки здесь представлены в виде объектов классов библиотеки iostream:
ifsteram - для ввода из файла;
ofsteram - для вывода в файл;
fsteram - для обмена с файлом в двух направлениях.
При этом стандартные потоки представлены объектами:
cin – стандартный поток ввода;
cout – стандартный поток вывода;
cerr – стандартный поток приема сообщений об ошибках.
Принцип работы с потоками аналогичен принятому в языке Си - программист создает объект-поток как экземпляр требуемого класса, связывая его с файлом на диске, после чего используя сервисные функции. Для чтения данных их потока можно использовать перегруженную операцию >>, для записи информации в поток используется операция <<. Принципиальное отличие объектно-ориентированного подхода Си++ заключается в том, что весь функционал работы с потоком сосредоточен в методах классов, которые должны вызываться в контексте объекта-потока. В листинге 4 приведен пример обмена данными программы с файлом на языке Си++.
#include <fstream.h>
…
fstream f; char str[20];
f.open("temp",ios::in|ios::out); //открываем поток на
//чтение и запись
f.write(“Тест”,4); //пишем в поток слово Тест
f.seekg(0); //перемещаем указатель потока в его начало
f>>str; //читаем строку из потока
cout<<str;
Таким образом, эффективное использование потокового ввода-вывода на языке Си++ должно опираться на знание методов и компонентных данных классов-потоков. Как видно из примера, названия методов во многом схожи с названиями функции библиотеки Си и разобраться в их назначении и способах применения будет совсем несложно. Более подробную информацию о потоковых классах языка Си++ можно почерпнуть в [1].
