
7_ Файлы .
...docint Num=i*w+j;
// Чтение элемента
double x;
for(int k=0; k<=Num;k++)
fscanf(pFile, "%lf", &x);
fclose(pFile);
return x;
}
6.5 Чтение и запись объектов абстрактных типов
Примерами объектов абстрактного типа являются структуры. Несмотря на то, что абстрактные типы определяются программистами, в стандартной библиотеке языка Си имеются функции, обеспечивающие запись в файл и чтения из него объектов таких типов.
Функция с именем fwrite() позволяет записывать в файл нужное количество однотипных объектов. Для хранения каждого объекта требуется одно и то же количество байтов (блок) памяти. Ее прототип выглядит следующим образом.
zise_t fwrite(
// Адрес первого блока с записываемым объектом
const char* Buffer,
// Размер одного блока в байтах
size_t Size,
// Количество записываемых блоков
size_t Count,
// Адрес структуры типа FILE
FILE* pFile);
Идентификатор size_t является синонимом идентификатора типа unsigned int, определенным с помощью команды typedef. Первым в файл записывается байт за байтом без каких-либо преобразований блок с адресом Buffer, преобразованным к типу char. Затем следующий блок. Функция возвращает количество записанных в файл блоков.
Для чтения из файла абстрактных объектов можно воспользоваться функцией с именем fread(). Ее прототип выглядит следующим образом.
zise_t fread(
// Адрес первого блока с читаемым объектом
const char* Buffer,
// Размер одного блока в байтах
size_t Size,
// Количество читаемых блоков
size_t Count,
// Адрес структуры типа FILE
FILE* pFile);
Она позволяет прочитать из файла с указателем pFile ровно Count объектов размером Size байтов каждый. Читаемые объекты размещаются в буфере с адресом Buffer, преобразованном к типу char, байт за байтом без каких-либо преобразований. Функция возвращает количество прочитанных из файла объектов.
Пример 6.5 - Чтение и запись объектов абстрактных типов
// Подключение файлов с прототипами функций
#include <stdio.h>
// Определение абстрактного типа
struct AA {
char ch;
int i;
float f;
};
void main(void) {
// Открытие файла для записи и чтения
FILE* pFile=fopen("Data.dat", "w+b");
// Проверка корректности результата
if(pFile==NULL) {
// Действия при неудачном открытии файла
return ;
}
// Создание объектов
AA aa={'A', 1, 2.0f},
bb={'B', 2, 3.0f};
// Запись в файл и вычисление значения текущей позиции
fwrite(&aa, sizeof(AA), 1, pFile);
printf("LenFile: %ld\n", ftell(pFile));
fwrite(&bb, sizeof(AA), 1, pFile);
printf("LenFile: %ld\n", ftell(pFile));
// Установка текущей позиции на начало файла
fseek(pFile, 0L, SEEK_SET);
// Чтение из файла
fread(&bb, sizeof(AA), 1, pFile);
fread(&aa, sizeof(AA), 1, pFile);
// Отображение прочитанных объектов
printf("aa={%c %d %f}\n", aa.ch, aa.i, aa.f);
printf("bb={%c %d %f}\n", bb.ch, bb.i, bb.f);
// Закрытие файла
fclose(pFile);
}
После выполнения приведенной программы на экране дисплея должно появиться следующее сообщение:
LenFile: 12
LenFile: 24
aa={B 2 3.000000}
bb={A 1 2.000000}
Отметим, что для хранения структуры из трех элементов типов char, int и float компилятор MS Visual C++ v.6.0 выделяет 12 байт, а не девять!
В следующем примере в файл записываются размеры и элементы матрицы. Затем из файла читается ее элемент из строки с номером i и столбца с номером j. Для чтения элемента написана отдельная функция ReadFromFile().
Пример 6.6 – Чтение из файла элемента таблицы
// Чтение из файла элемента i-ой строки и j-ого столбца таблицы
#include <stdio.h>
#include <stdlib.h>
double ReadFromFile(char* FileName, int i, int j);
void main(void){
// Размеры матрицы
int h=3, w=4;
// Матрица
double A[]={0.0, 0.1, 0.2, 0.3,
1.0, 1.1, 1.2, 1.3,
2.0, 2.1, 2.2, 2.3};
// Открытие файла для записи
FILE* pFile=fopen("Matrix.dat", "wb");
if(!pFile){
printf("The file for a record was not opened!\n");
return;
}
// Запись размеров
fwrite((char*)&h, sizeof(int), 1, pFile);
fwrite((char*)&w, sizeof(int), 1, pFile);
printf("%d\n", ftell(pFile));
// Запись матрицы
fwrite((char*)A, sizeof(double), h*w, pFile);
printf("%d\n", ftell(pFile));
fclose(pFile);
printf("Ready...\n");
// Пауза
getchar();
// Чтение элемента матрицы a[i][j]
int i, j;
printf("Reading of an element of a matrix\n");
printf(" Number of a line: ");
scanf("%d", &i);
printf(" Number of a column: ");
scanf("%d", &j);
double Res=ReadFromFile("Matrix.dat", i, j);
printf("A[%d][%d]=%f\n", i, j, Res);
}
double ReadFromFile(char* FileName, int i, int j){
// Открытие файла для чтения
FILE* pFile=fopen(FileName, "rb");
if(!pFile){
printf("The file for reading was not opened!\n");
return 0;
}
// Чтение размеров
int h,w;
fread((char*)&h, sizeof(int), 1, pFile);
fread((char*)&w, sizeof(int), 1, pFile);
// Проверка правильности задания индексов
if(i<0||NumLine<=i){
printf("Number of a line is not correct!\n");
exit(-1);
}
if(j<0||NumCol<=j){
printf("Number of a column is not correct!\n");
exit(-1);
}
// Вычисление смещения
int offset=(i*NumCol+j)*sizeof(double)+2*sizeof(int);
// Чтение элемента
double x;
fseek(pFile, offset, 0);
fread((char*)&x, sizeof(double), 1, pFile);
fclose(pFile);
return x;
}
6.6 Запись и чтение строк
Для записи строки в файл можно воспользоваться стандартной функцией fputs() c прототипом
int fputs(
// Адрес строки
const char* szBuffer,
// Адрес структуры типа FILE
FILE* pFile);
Ее первый параметр является адресом записываемой в файл стоки, а второй - адресом структуры типа FILE, полученным при открытии файла. Так как ‘\0‘-символ, которым заканчивается каждая строка, функция fputs() в файл не записывает, то при создании текстовых файлов после ввода каждой строки программист должен предусмотреть запись символа ‘\n‘ - новая строка. Функция возвращает объект типа int. Его значение зависит от конкретной модели компилятора. Для MS Visual C++ 4.0, например, при успешном завершении функция возвращает ненулевое значение, а в остальных случаях - константу EOF.
Рассмотрим приложение, обеспечивающее создание текстового документа и сохранение его на диске. Документ вводится с клавиатуры и отображается на экране предложение за предложением. Одновременно каждое введенное предложение записывается в файл.
Пример 6.7 - Создание текстового файла с клавиатуры
#include <stdio.h>
#include <process.h>
void main(void) {
// Буфер для записываемой в файл строки
char szBuffer[81];
// Открытие текстового файла для записи
FILE* pFile=fopen("MyFile.txt", "wt");
// Проверка корректности результата операции
if(!pFile) {
// Действия при неудачном открытии файла
printf("The file to open it was not possible!\n");
return ;
}
// Действия при корректном открытии файла
while(1) {
// Предупреждение о вводе с клавиатуры
printf("Input of a string or command \”Enter!\” \n");
// Ввод строки или клавиши Enter с клавиатуры
gets(szBuffer);
// Анализ введенной информации
if(szBuffer[0]==(char)NULL) {
// Действия при нажатии Enter (вводе пустой строки)
printf("The file is created!\n");
break;
}
// Действия при вводе строки
else {
// Запись строки в файл
fputs(szBuffer, pFile);
// Запись в файл символа '\n'
fputc('\n', pFile);
}
}
// Закрытие файла
fclose(pFile);
}
Для чтения строки из текстового файла можно воспользоваться стандартной функцией с именем fgets() и прототипом
char* fgets(
// Адрес буфера для читаемой строки
char* szBuffer,
// Число читаемых символов (байтов)
int n,
// Адрес структур типа FILE
FILE* pFile);
Ее первым параметром является адрес буфера, в который записывается читаемая строка. Второй параметр определяет максимальное количество читаемых из файла байтов, а третий - адрес структуры типа FILE*, полученный при открытии файла. Чтение из файла продолжается до тех пор, пока не наступит любое из следующих трех событий: будет прочитан символ ‘\n‘ - новая строка, будут прочитаны n-1 объектов типа char или будет достигнут конец файла. Концом текстового файла считается байт, содержащий константу EOF. После завершения чтения из файла к уже прочитанным байтам в буфер добавляется ‘\0‘-символ. Если n=1, то формируется пустая строка. При успешном завершении операции функция возвращает адрес буфера с прочитанной строкой, а в случае возникновения ошибки или достижения конца файла она возвращает NULL типа char*.
Пример 6.8 - Чтение из файлов строк заданной длины
#include <stdio.h>
#include <string.h>
// Прототип функции чтения текстовых файлов
int ReadTextFile(
// Строка с именем файла
const char* szFileName,
// Длина строки
int StrLen,
// Число строк на странице
int PageSize);
void main (void) {
// Имя файла для чтения
char szFileName[]="MyFile.txt";
// Длина строки
int StrLen=7;
// Число строк
int PageSize=2;
// Чтение из файла
int iResponse=ReadTextFile(szFileName, StrLen, PageSize);
// Анализ результата выполнения чтения
if(!iResponse)
printf("\nError!");
else
printf("\nOk\n");
}
// Определение функции ReadTextFile()
int ReadTextFile(const char* szFileName, int StrLen, int PageSize) {
// Буфер для прочитанных объектов
char* szBuffer=new char[StrLen+1];
// Открытие текстового файла для чтения
FILE* pFile=fopen(szFileName, "rt");
// Проверка корректности выполнения операции
if(!pFile)
// Действия при неудачном открытии файла
return 0;
// Номер строки и страницы в документе
int LineCount=0, PageCount=0;
// Чтение строк в буфер
while(fgets(szBuffer, StrLen+1, pFile)>(char*)0){
// Анализ и коррекция строки
for(int i=0; (szBuffer[i]!='\n')&&(i<StrLen); i++);
// Пустой цикл
if(i==0)
// Действия при чтении пустой строки
continue;
if(szBuffer[i]=='\n')
// Укороченная строка
szBuffer[i]='\0';
// Вычисление и печать номера страницы
if(((LineCount+PageSize)/PageSize)>PageCount){
PageCount++;
printf("\n");
for(int k=0; k<StrLen; k++)
printf(" ");
printf("%d\n", PageCount);
}
LineCount++;
puts(szBuffer);
}
// Действия при достижении конца файла
fclose(pFile);
delete[] szBuffer;
return 1;
}
/*
8.4. Запись в файл и чтение из него объектов типа char
Для хранения объектов типа char требуется минимальный объем памяти равный одному байту. Для того, чтобы записать в файл объект типа char, можно воспользоваться стандартной функцией fputc() с прототипом
int fputc(
// Записываемый в файл объект
int c,
// Адрес структуры типа FILE
FILE* pFile);
Ее первым параметром является записываемый объект. В соответствии с правилами вызова функций ASCII-код записываемого объекта преобразуется к типу int. Вторым параметром является адрес структуры типа FILE, полученный при открытии файла. При успешном завершении функция возвращает ASCII-код записываемого символа, преобразованный к типу int. В остальных случаях возвращаемым значением будет константа EOF.
Пример 8.2. Запись в файл объектов типа char
#include <stdio.h>
#include <stdlib.h>
void main(){
// Текст для записи в файл: 22 символа, не считая ‘\0’-символа
char psz[]="Writing and computing\n";
// Открытие файла
FILE* pFile=fopen("Text.asc", "wt");
// Проверка
if(!pFile){
printf("Error! The file is not opened\n");
exit(1);
}
// Запись в файл
int i=0;
while(psz[i]!='\0')
// Запись символа
fputc(psz[i++], pFile);
// Длина файла
printf("FileLenght=%d\n", ftell(pFile));
// Закрытие файла
fclose(pFile);
}
После выполнения приложения на экране дисплея появится следующее сообщение:
FileLenght=23
Оно означает, что кроме 22 символов, записанных в файл программистом, там оказался еще один байт. Это связано с тем, что управляющая последовательность ‘\n’ при записи в текстовый файл заменяется парой из ‘\r’ и ‘\n’. Поскольку путь к файлу не был указан, то он будет создан в том же каталоге, в котором находится само приложение.
Для чтения из файла объектов типа char можно использовать стандартную функцию с именем fgetc() и прототипом
int fgetc(
// Адрес структуры типа FILE
FILE* pFile);
Ее единственным параметром является адрес структуры типа FILE, полученный при открытии файла. Функция возвращает ASCII-код прочитанного объекта типа char, преобразованный к типу int. При попытке чтения константы EOF или возникновении ошибки возвращается EOF.
При чтении из файла бывает полезна функция с прототипом
int feof(
// Адрес структуры FILE
);
позволяющая определить достижение конца файла. Она возвращает отличное от нуля значение, если внутренний указатель файла совпадает с его длиной. В остальных случаях – нуль.
Ниже предлагается приложение, обеспечивающее запись в файл объектов типа char, вводимых с клавиатуры, и копирование созданного файла. Для ввода символа используется стандартная функция с прототипом
int getche();
Она возвращает ASCII-код введенного с клавиатуры символа, преобразованный к типу int, и отображает его на экране дисплея. При использовании функциональных клавиш или клавиш управления курсором для получения ASCII-кода необходимо вызывать функцию дважды. В качестве признака завершения ввода выбрана комбинация клавиш Ctrl+z c ASCII-кодом равным ‘\32’.
Пример 8.3. Запись объектов типа char и копирование файла
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void main(){
// Открытие файла для записи и чтения
FILE* pSrc=fopen("Text.asc", "w+t");
// Проверка
if(!pSrc){
printf("Error! The file is not opened\n");
exit(1);
}
// Ввод и запись в файл
int ch;
printf("Input: ");
while((ch=getche())!='\32'){
// Запись символа
fputc(ch, pSrc);
}
printf("\n\n");
// Длина файла
printf("FileLenght=%ld\n", ftell(pSrc));
// Открытие файла для записи
FILE* pDest=fopen("Copy.asc", "wt");
if(!pDest){
printf("Error: not open file!\n");
exit(2);
}
// Подготовка к копированию
int temp;
rewind(pSrc);
// Копирование
while((temp=fgetc(pSrc))!=EOF)
fputc(temp, pDest);
// Закрытие
fclose(pSrc);
fclose(pDest);
printf("Ready\n");
}