
6.3 Форматированный ввод/вывод (текстовые файлы)
Запись и чтение текстовых файлов внешне очень похоже на консольный ввод/вывод (т.е. вывод на экран и ввод с клавиатуры). Его особенностью является то, что каждая переменная записывается и читается по отдельности. Для этих целей существует множество специализированных библиотечных функций для работы с переменными различных типов данных. Функции fprintf() и fscanf() можно использовать вместо любой из этих функций, поскольку они универсальны, т. к. позволяют работать с данными любого типа. Интерфейс этих функций следующий:
int fprintf ( FILE *уф, const char *управляющая_строка, ...);
int fscanf ( FILE *уф, const char *управляющая_строка, ...);
где уф – указатель файла, возвращаемый в результате вызова fopen(). Операции ввода/вывода функции fprintf() и fscanf() выполняют с тем файлом, на который указывает уф. Иными словами, параметрами этих функций являются файловая переменная, управляющая строка и список вывода.
В качестве иллюстрации работы с текстовым файлом приведём фрагмент программы для записи матрицы в файл. Последовательность записи данных следующая: сначала будет записана размерность матрицы (число строк и столбцов), а затем сама матрица построчно. Если не указывается путь доступа к файлу, то он будет сохранён в той же папке, что и программа.
FILE *fp;
char FileName[16];
printf ( "\n Enter file name " );
scanf ( "%s", FileName );
fp = fopen ( FileName, "wt");
if ( fp == NULL ) {
printf ( "\n Error opening the file\n");
exit(1);
}
fprintf ( fp, "%5d%5d\n", N, M ); // запись размерности
for ( int i = 0; i < N; i++ )
{
for ( int j = 0; j < M; j++ )
fprintf ( fp, "%5d", Mas[i][j]); // запись матрицы
fprintf ( fp, "\n");
}
fclose (fp);
Далее приведём аналогичный фрагмент программы для чтения матрицы из файла. Данные должны читаться в том же порядке, что и записывались: сначала размерность, затем сама матрица.
FILE *fp;
char FileName[16];
printf ( "\n Enter file name " );
scanf ( "%s", FileName );
fp = fopen ( FileName, "rt");
if ( fp == NULL ) {
printf ( "\n Error opening the file\n");
exit(1);
}
fscanf ( fp, "%d%d ", &N, &M ); // чтение размерности
for ( int i = 0; i < N; i++ )
for ( int j = 0; j < M; j++ )
fscanf ( fp, "%d", &Mas[i][j]); // чтение матрицы
fclose (fp);
6.4 Блоковый ввод/вывод (двоичные файлы)
Для чтения и записи двоичных файлов в файловой системе языка С имеется две функции: fread() и fwrite(). Эти функции позволяют читать и записывать блоки данных любого типа. Их прототипы следующие:
size_t fread (void *буфер, size_t колич_байт, size_t счетчик, FILE *уф);
size_t fwrite (const void *буфер, size_t колич_байт, size_t счетчик, FILE *уф);
Для fread() буфер – это указатель на область памяти, в которую будут прочитаны данные из файла. А для fwrite() буфер – это указатель на данные, которые будут записаны в файл. Значение счетчик определяет, сколько считывается или записывается элементов данных, причем длина каждого элемента в байтах равна колич_байт. (Тип size_t определяется как одна из разновидностей целого типа без знака.) И, наконец, уф – это указатель файла, то есть на уже открытый поток.
Функция fread() возвращает количество прочитанных элементов. Если достигнут конец файла или произошла ошибка, то возвращаемое значение может быть меньше, чем счетчик. А функция fwrite() возвращает количество записанных элементов. Если ошибка не произошла, то возвращаемый результат будет равен значению счетчик.
Более просто список параметров этих функций можно представить следующим образом: указатель на область оперативной памяти, размер блока, количество блоков и файловая переменная.
В качестве иллюстрации работы с двоичным файлом приведём фрагменты программ для записи в файл и чтения из файла одномерного массива. Для краткости функции открытия fopen() и закрытия fclose() файла опущены.
Запись в файл:
1) поэлементная запись
const int Nmax = 10; // максимальная размерность массива
int Mas[Nmax];
int N; // реальная размерность массива
for ( int i = 0; i < N; i++ )
fwrite( Mas, sizeof(Mas[i]), N, fp );
Вместо Mas можно использовать &Mas[0] , а вместо sizeof(Mas[i]) – sizeof(int).
2) блоковая запись – 1-й вариант. Здесь в качестве размера блока указан размер одного элемента массива sizeof(int), а в качестве количества блоков – количество элементов массива N.
fwrite( Mas, sizeof(int), N, fp );
3) блоковая запись – 2-й вариант. Здесь в качестве размера блока указан размер всего массива sizeof(int) * N, а количество блоков, соответственно, равно единице.
fwrite( Mas, sizeof(int) * N, 1, fp );
4) блоковая запись – 3-й вариант. Используется тогда, когда реальная размерность массива совпадает с максимальной.
fwrite( Mas, sizeof(Mas), 1, fp );
Пример чтения из файла представлен для случая, когда известен тип массива Mas (например, int), но не известно количество элементов. В примере использовано то обстоятельство, что функция fread() является типизированной и возвращает количество прочитанных блоков.
int Mas[Nmax];
int i, temp, N;
i = 0;
while ( fread( &temp, sizeof(int), 1, fp ) != 0)
{
Mas[i] = temp;
i++;
}
N = i;
Поскольку размерность массива, записанного в файл, не известна, чтение продолжается до тех пор, пока не будет достигнут конец файла. До начала цикла номер элемента массива i принимает начальное значение. Затем очередной элемент массива считывается из файла в промежуточную переменную temp. Если чтение было успешным, то функция fread() возвращает 1 (т.е. количество прочитанных элементов). Тогда прочитанное значение копируется из переменной temp в соответствующий элемент массива Mas[i], после чего номер элемента i увеличивается на единицу. Если же был достигнут конец файла и очередной элемент прочесть не удалось, то функция fread() возвращает 0, и цикл чтения массива из файла завершается. При этом количество реально прочитанных элементов N будет равно текущему значению переменной i.