ТЕМА 14. Файлы

advertisement
ТЕМА 14. ФАЙЛЫ
Понятие файла. Процедуры для работы с файлами – открытие и закрытие файлов. Создание и работа с текстовыми файлами. Основные функции чтения-записи
14.1.Понятие файла
Различают два вида файлов: текстовые и двоичные.
Текстовые файлы хранят информацию в виде последовательности символов. Вывод осуществляется аналогично выводу на экран. Текстовые файлы
могут быть отредактированы в любом текстовом редакторе. В текстовом режиме каждый разделительный символ строки автоматически преобразуется в
пару (возврат каретки – переход на новую строку).
Например строка “Privet\nstudent\n”
В памяти хранится:
P r i v e t \10 s t u d e n t \10 \0
В текстовом файле хранится:
P r i v e t \10\13 s t u d e n t \10 \13
Бинарные (или двоичные) файлы предназначены для хранения только
числовых значений данных. Структура такого файла определяется программно.
Дисковый файл – это поименованное место на носителе информации.
Файлы, размещаемые на магнитных носителях, имеют следующую
структуру:
BOF
0
1
2
n-2
n-1
EOF
В начале файла записана информация о файле BOF (Begin of File), его имя,
тип, длина и т.д., в конце файла помещается признак конца файла EOF (End of
File). Если файл пуст, то BOF и EOF совмещены, а указатель установлен в
ноль.
14.2. Функции для работы с файлами
Функции для работы с файлами размещены в библиотеках stdio.lib
(#include < stdio.h >) и io.lib (#include < io.h >). Каждый файл должен быть
связан с некоторым указателем. Этот указатель имеет тип FILE и используется во всех операциях с файлами.
Формат объявления указателя на файл следующий:
FILE *указатель на файл;
Например:
FILE *fl1, *fl2;
Указатель содержит адрес структуры, включающей в себя различные сведения о файле, например, его имя, статус и указатель на начало файла. Чтобы
выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов.
1
Макрос NULL определяет пустой указатель.
Макрос EOF, часто определяемый как -1, является значением, возвращаемым тогда, когда функция ввода пытается выполнить чтение после конца
файла.
Макрос FOPEN_MAX определяет целое значение, равное максимальному
числу одновременно открытых файлов.
14.2.1. Функции для открытия-закрытия файла
Прототип функции:
FILE *fopen(const char *имя_файла,
const char *режим_открытия);
Функция открывает файл и связывает его с потоком. Возвращает указатель на открытый файл.
Имя_файла — это указатель на строку символов, в которой хранится имя
файла и путь к нему. Например: “d:\\work\\lab2.dat”.
режим_открытия это указатель на строку символов, в которой указывается режим открытия файла. Допустимы режимы указаны в таблице:
r
Открыть текстовый файл для чтения. Если файл с указанным
именем отсутствует, то возникает ошибка
w
Создать текстовый файл для записи. Если файл с указанным
именем уже существует, то прежняя информация уничтожается
a
Добавить информацию в конец текстового файла
rb
Открыть двоичный файл для чтения. Если файл с указанным
именем отсутствует, то возникает ошибка
wb
Создать двоичный файл для записи. Если файл с указанным
именем уже существует, то прежняя информация уничтожается
ab
Добавить информацию в конец двоичного файла
r+
Открыть текстовый файл для чтения/записи
w+
Создать текстовый файл для чтения/записи
a+
Добавить в конец текстового файла или создать текстовый файл
для чтения/записи
r+b или
rb+
Открыть двоичный файл для чтения/записи
w+b или
wb+
Создать двоичный файл для чтения/записи
2
a+b или
ab+
Добавить в конец двоичного файла или создать двоичный файл
для чтения/записи
По умолчанию файл открывается в текстовом режиме.
Если при открытии файла произошла ошибка, функция fopen возвращает
значение NULL.
Для создания файла можно записать:
FILE *fl;
fl = fopen("lab2.dat","w”);
Более грамотно бедут:
FILE *fl;
if ((fl = fopen("lab2.dat","w"))==NULL)
{
cout << "Oshibka pri sozdanii"<<endl;
return 1;
}
Такой алгоритм позволяет обнаружить любую ошибку, возникающею при
создании файла.
Для исключения ошибки, возникающей при открытии несуществующего
файла, можно использовать конструкцию:
FILE *fl;
if ((fl = fopen("lab2.dat","r"))==NULL)
{
fl = fopen("lab2.dat","w");
}
При попытке открыть несуществующий файл в режиме дозаписи, он будет
автоматически создан.
Разница между режимами r+ и w+ состоит в том, что если файл не существует, то в режиме открытия r+ он создан не будет, а в режиме w+ будет создан! Если файл уже существует, то открытие его в режиме w+ приведет к
утрате его содержимого, а в режиме r+ оно останется нетронутым.
При записи обмен происходит не непосредственно с файлом, а с некоторым буфером. Информация из буфера переписывается в файл только при переполнении буфера или при закрытии файла.
Для закрытия файла используется функция fclose(). Прототип функции:
int fclose(FILE *указатель_на _файл);
Функция закрывает поток, который был открыт с помощью вызова fopen()
и записывает в файл все данные, которые еще оставались в дисковом буфере.
Доступ к файлу после выполнения функции будет запрещен.
Возвращение нуля означает успешную операцию закрытия. В случае же
ошибки возвращается EOF. Чтобы точно узнать, в чем причина этой ошибки,
можно использовать стандартную функцию ferror().
Для закрытия нескольких файлов введена функция:
3
int fcloseall(void);
Функция закрывает все открытые файлы. Возвращает количество закрытых файлов или EOF, если возникает ошибка. В MVC++ 2005 функция пишется: _fcloseall.
14.2.1. Функции для модификации содержимого файла
Функция
int putc(int символ, FILE * указатель_на _файл);
записывает один символ в текущую позицию указанного открытого файла.
Хотя символ и определяется как int, однако записывается только младший
байт (соответствующий char). Если функция выполнилась успешно, то возвращается записанный символ, иначе – EOF.
Функция
int getc(FILE * указатель_на _файл);
читает один символ из текущей позиции указанного открытого файла. После чтения указатель сдвигается на одну позицию вперед. Если достигнут конец файла, то функция возвращает значение EOF.
Пример: Открыть файл и записывать туда вводимые с клавиатуры символы, до тех пор, пока не будет введен символ ‘!’. Прочитать полученный файл.
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
FILE *fl;
void flr();
void flw();
int main()
{
flr(); // Запись в файл
flw(); // Чтение из файла
return 0;
}
void flr()
{
if ((fl = fopen("lab2.dat","w"))==NULL)
{
cout << "Oshibka pri sozdanii"<<endl;
exit(1);
}
4
char ch;
do {
cin >> ch;
putc(ch, fl);
} while (ch != '!');
fclose(fl);
}
void flw()
{
if ((fl = fopen("lab2.dat","r"))==NULL)
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
char ch= getc(fl);
while (ch != EOF)
{
cout << ch << " ";
ch=getc(fl);
}
fclose(fl);
}
Функция
int feof(FILE * указатель_на _файл);
возвращает отличное от нуля значение (true) если достигнут конец файла,
и ноль (false), если конец файла не достигнут. Функция может работать с любыми типами файлов.
Функция flw в предыдущем примере может выглядеть так:
void flw()
{
if ((fl = fopen("lab2.dat","r"))==NULL)
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
while(!feof(fl)) cout << getc(fl) << " ";
fclose(fl);
}
5
Функция
int fputs(const char * строка, FILE * указатель_на _файл);
записывает строку символов в текущую позицию указанного открытого
файла. В случае ошибки эта функция возвращает EOF. Нулевой символ в
файл не записывается.
Функция
char *fgets(char *строка, int длина,
FILE * указатель_на _файл);
читает строку символов из текущей позиции указанного открытого файла
до тех пор, пока не будет прочитан символ перехода на новую строку, или
количество прочитанных символов не станет равным длина-1. Если был прочитан разделитель строк, он записывается в строку. Полученная в результате
строка будет оканчиваться символом конца строки ('\0'). В случае ошибки
функция возвращает NULL
Пример: Открыть файл и записывать туда все введенные с клавиатуры
строки, до тех пор, пока не будет введена пустая строка. Прочитать полученный файл.
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *fl;
void fsr();
void fsw();
int main()
{
fsr(); // Запись в файл
fsw(); // Чтение из файла
return 0;
}
void fsr()
{
if ((fl = fopen("lab2.dat","w"))==NULL)
{
cout << "Oshibka pri sozdanii"<<endl;
exit(1);
}
cout << "Vvedite sytoki" << endl;
6
char str[80];
do {
gets(str);
strcat(str, "\n"); // добавление разделителя строк
fputs(str, fl);
} while(*str!='\n');
fclose(fl);
}
void fsw()
{
if ((fl = fopen("lab2.dat","r"))==NULL)
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
char str[80];
while(!feof(fl))
{
if( fgets( str, 100, fl ) != NULL) cout << str;
}
fclose(fl);
}
Кроме основных функций ввода/вывода, также часто используются функции fprintf() и fscanf().
Функция
int *fprintf(FILE * указатель_на _файл,
const char * управляющая_строка);
записывает форматированные данные в файл. Управляющая_строка определяет строку форматирования аргументов, заданных своими адресами.
Обычно эта строка состоит из последовательности символов “%”, после которых следует символ типа данных:
I или i
D или d
U или u
E или e
s
c
7
Десятичное, восьмеричное или шестнадцатеричное целое
Десятичное целое
Десятичное целое без знака
Действительное с плавающей точкой
Строка символов
Символ
Функция
int *fscanf(FILE * указатель_на _файл,
const char * управляющая_строка);
читает форматированные данные из файла. Строка форматирования строится аналогично функции fprintf.
Следует обратить внимание на то, что при чтении данных всегда указываются адреса переменных (&), а не сами переменные.
Пример Запись и чтение данных из файла.
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *fl;
double v;
char st[81];
int n;
char ch;
void fpr();
void fpw();
int main()
{
fpr(); // Запись в файл
fpw(); // Чтение из файла
return 0;
}
void fpr()
{
if ((fl = fopen("lab2.dat","w"))==NULL)
{
cout << "Oshibka pri sozdanii"<<endl;
exit(1);
}
v=3.14;
n=298;
fprintf( fl,"%s %e %d %c", "string",v,n,'x');
fclose(fl);
}
void fpw()
{
if ((fl = fopen("lab2.dat","r"))==NULL)
8
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
fscanf(fl,"%s %e %d %c",st,&v,&n,&ch);
cout << st << endl;
cout << v << endl;
cout << n << endl;
cout << ch << endl;
fclose(fl);
}
Функция
void rewind(FILE * указатель_на _файл);
устанавливает указатель текущей позиции выделенного файла в начало
файла.
Функция
int ferror(FILE * указатель_на _файл);
определяет, произошла ли ошибка во время работы с файлом. Она возвращает ненулевое значение (true), если при последней операции с файлом
произошла ошибка; в противном же случае она возвращает 0 (false).
Функция
size_t fwrite(const void * записываемое_данное,
size_t размер_элемента, size_t число_элементов,
FILE *указатель_на _файл);
записывает в файл заданное число данных заданного размера. Размер данных задается в байтах. Тип size_t определяется как целое без знака. Функция
возвращает число записанных элементов. Если число записанных элементов
не равно заданному, то возникла ошибка.
Функция
size_t fread(void * считываемое_данное,
size_t размер_элемента, size_t число_элементов,
FILE *указатель_на _файл);
считывает в указанное данное заданное число данных заданного размера.
Размер данных задается в байтах. Функция возвращает число прочитанных
элементов. Если число записанных элементов не равно заданному, то возникла ошибка.
Функции fread() и fwrite() при открытии файла для работы с двоичными
данными могут работать с информацией любого типа.
Функция
int fileno(FILE * указатель_на _файл);
9
возвращает значение дескриптора указанного файла (дескриптор – логический номер файла для заданного потока).
Функция
long filelength(int дескриптор);
возвращает длину файла с соответствующим дескриптором в байтах.
Функция
int chsize(int дескриптор, long размер);
устанавливает новый размер файла с соответствующим дескриптором. Если размер файла увеличивается, то в конец добавляются нулевые символы,
если размер файла уменьшается, то все лишние данные удаляются.
Функция
long ftell(FILE * указатель_на _файл);
возвращает значение указателя на текущую позицию файла.
Функция
int fgetpos(FILE *stream, fpos_t *позиция);
определяет значение текущей позиции файла.
Функция
int fseek(FILE * указатель_на _файл, long int число_байт, int
точка_отсчета);
устанавливает указатель в заданную позицию. Заданное количество байт
отсчитывается от начала отсчета , которое задается следующими макросами:
начало файла – SEEK_SET, текущая позиция – SEEK_CUR, конец файла –
SEEK_END. При успешном завершении работы функция возвращает нуль, а в
случае ошибки — ненулевое значение.
Пример: Открыть файл и записывать туда все вводимые с клавиатуры
числа, до тех пор, пока не будет введено число -1. Отсортировать данные в
файле по возрастанию.
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
FILE *fl;
void fbr();
void fbsort();
void fbw();
int main()
{
fbr(); // Запись в файл
fbsort(); // Сортировка в файле
fbw(); // Чтение из файла
return 0;
}
10
void fbr()
{
if ((fl = fopen("lab2.dat","wb"))==NULL)
{
cout << "Oshibka pri sozdanii"<<endl;
exit(1);
}
int a;
do {
cin >> a;
int nwrt = fwrite( &a, sizeof(int), 1, fl );
} while (a != -1);
fclose(fl);
}
void fbsort()
{
if ((fl = fopen("lab2.dat","rb+"))==NULL)
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
int nb =sizeof(int), a, b, nwrt;
int n=filelength(fileno(fl))/nb;
for (int i=0; i<n-1; i++)
for (int j=i+1; j<n; j++)
{
fseek(fl ,i*nb , SEEK_SET);
fseek(fl ,j*nb , SEEK_SET);
if (a>b)
{
fseek(fl ,i*nb , SEEK_SET);
fseek(fl ,j*nb , SEEK_SET);
}
}
fclose(fl);
}
void fbw()
{
11
nwrt = fread( &a, nb, 1, fl );
nwrt = fread( &b, nb, 1, fl );
nwrt = fwrite( &b, nb, 1, fl );
nwrt = fwrite( &a, nb, 1, fl );
if ((fl = fopen("lab2.dat","rb"))==NULL)
{
cout << "Oshibka pri otritii"<<endl;
exit(1);
}
int a;
while(true)
{
int nwrt = fread( &a, sizeof(int), 1, fl );
if (nwrt!=1) break;
cout << a << " ";
}
fclose(fl);
}
12
Download