Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Текст_лекций_Си.doc
Скачиваний:
18
Добавлен:
16.03.2016
Размер:
1.06 Mб
Скачать

Глава 10 фAйлы

Файл характеризуется именем, расширением, версией.

MS DOS: длина имени <=8 символов, длина расширения <=3 символов.

Файл: = <имя> [.<расширение>]

Proba.c

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

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

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

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

Функции библиотеки ввода-вывода языка СИ, поддерживающие обмен данными с файлом на уровне потока, позволяют обрабатывать данные различных размеров и форматов, обеспечивая при этом буферизированный ввод и вывод. Таким образом,

поток – это файл вместе с предоставляемыми средствами буферизации.

Файл + средства_буферизации = поток

При работе с потоком можно производить следующие действия:

  • Открывать и закрывать потоки (связывать указатели на потоки с конкретными файлами).

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

  • Анализировать ошибки потокового ввода–вывода и условие достижения конца потока (конца файла).

  • Управлять буферизацией потока и размером буфера.

  • Получать и устанавливать указатель (индикатор) текущей позиции в потоке.

Для того, чтобы можно было использовать функции библиотеки ввода–вывода языка СИ, в программу необходимо включить заголовочный файл stdio.h (#include<stdio.h>), который содержит прототипы функций ввода–вывода, а также описания констант, типов и структур, необходимых для работы функций обмена с потоком.

    1. Открытие и закрытие потока

Подготовительные операции перед обменом программы с файлом:

  1. Проверка наличия файла с данным именем при чтении.

  2. Наличие свободного пространства на диске при создании файла.

  3. Позиционирование файла.

При благополучном открытии файла ОС возвращает целое число, характеризующее этот поток и называемое ДЕСКРИПТОМ файла (потока).

FORTRAN READ(5,…) WRITE(6,…)

Процесс отсоединения файла от программы после окончания операций с ним называется закрытием файла. При этом освобождаются ресурсы (в основном память). С каждым потоком связан системный буфер, который для MS DOS составляет 512 байтов. Дескриптор – это тоже ресурс, поскольку их число для MS DOS ограничено 255. Также для каждого файла в памяти хранится его описание в виде структуры типа FILE, описанного в файле stdio.h.

РЕКОМЕНДАЦИЯ. Желательно закрывать файл явно по причинам:

  1. Случайной порчи открытого файла.

  2. Возможного повторного открытия того же файла с другим способом обработки.

  3. При аварийном завершении программы информация, хранимая в системном буфере пропадает.

  4. Возможность сэкономить на количестве открываемых потоков.

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

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

Указатель на поток, например fp, должен быть (1) объявлен в программе следующим образом:

#include <stdio.h>

FILE *fp;

Указатель на поток (2) приобретает значение в результате выполнения функцией открытия потока:

Формат:

<указатель_на_поток> = fopen (<имя_файла>, <режим_открытия>);

Параметры <имя_файла> и <режим_открытия> являются указателями на массивы символов (строки!), содержащие, соответственно, имя файла, связанного с потоком, и строку режимов открытия. Однако, эти параметры могут задаваться и непосредственно в виде строк при вызове функции открытия файла.

Например: fp = fopen ( “t.txt”, ”r” );

где t.txt –имя некоторого файла, связанного с потоком; r – обозначение одного из режимов работы с файлом (тип доступа к потоку).

Стандартный текстовый файл можно открыть в одном из следующих шести режимов:

w –новый текстовый (см. ниже) файл открывается для записи. Если файл уже существовал, то предыдущее содержимое стирается и файл создается заново.

r” – существующий текстовый файл открывается только для чтения.

a” – текстовый файл открывается (или создается, если файла нет) для добавления в него новой порции информации (добавление в конец файла). В отличие от режима “w” режим “a” позволяет открывать уже существующий файл, не уничтожая его предыдущей версии, и писать в продолжение файла.

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

r+” – существующий текстовый файл открывается как для чтения, так и для записи в любом месте файла, однако в этом режиме невозможна запись в конец файла, т.е. недупустимо увеличение размеров файла.

a+” – текстовый файл открывается или создается (если файла нет) и становится доступным для изменений, т.е. для записи и для чтения в любом месте; при этом в отличие от режима “w+” можно открыть существующий файл и не уничтожать его содержимое; в отличие от режима “r+” в режиме “a+” можно ввести запись в конец файла, т.е. увеличивать его размер.

Поток можно открыть в текстовом или двоичном (бинарном) режиме. В текстовом режиме прочитанная из потока комбинация символов CR (значение 13) и LF (значение 10), т.е. управляющие коды операции “возврат каретки” и “перевод строки”, преобразуется в один новый символ ‘\n (значение 10, совпадающее с LF). При записи в поток в текстовом режиме осуществляется обратное преобразование, т.е. символ новой строки ‘\n’ (LF) заменяется последовательностью CR и LF.

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

Например “r+b” или “wb”. В некоторых компиляторах текстовый режим обмена обозначается буквой t, т.е. записывают “a+t” или “rt”.

Если поток открыт для изменений, т.е. в параметре режима присутствует символ “+”, то разрешены как чтение из потока так и запись в него. Однако смена режима (переход от записи к чтению и обратно) должна происходить только после установки указателя потока в нужную позицию.

При открытии потока могут возникнуть следующие ошибки:

1. Указанный файл, связанный с потоком, не найден (для режима чтения).

2. Диск заполнен или защищен от записи и т.п.

3. Необходимо также отметить, что при выполнении функции fopen() происходит выделение динамической памяти. При ее отсутствии устанавливается признак ошибки ”Not entough memory” (недостаточно памяти).

В перечисленных случаях указатель на поток приобретает значение NULL. Заметим. Что указатель на поток в любом режиме, отличном от аварийного, никогда не бывает равным NULL.

Приведем типичную последовательность операторов, которая используется при открытии файла, связанного с потоком:

if ( ( fp=fope(“t.txt”,”w”)) = = NULL ) {

perror(“ошибка при открытии файла t.txt \n”);

exit (0); }

где NULL – нулевой указатель, определенный в файле stdio.h.

Для вывода на экран дисплея сообщения об ошибке при открытии потока используется стандартная библиотечная функция perror( ), прототип которой в stdio.h имеет вид: void perror ( const char *s);

Функция perror( ) выводит строку символов, адресуемую указателем s, за которой размещаются: двоеточие, пробел и сообщение об ошибке. Содержимое и формат сообщения определяются реализацией системы программирования. Текст сообщения об ошибке выбирается функцией perror() на основании номера ошибки. Номер ошибки заносится в переменную int erno (определяется в заголовочном файле erno.h) рядом функций библиотеки языка СИ, в том числе и функциями ввода-вывода.

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

Открытые на диске файлы рекомендуется закрывать явно. Для этого используется библиотечная функция.

int fclose (<указатель_на_поток>) ;

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

Пример:

FILE *f, *fp;

if (( f=fopen (“inp3.txt”,”rt”)) = = NULL ) {

printf(“cannot open input file.\n”);

return 1; }

fscanf ( f, ”%d”, &count );

fclose ( f );

if ( !(fp = fopen ("inp3.txt", "r") ) )

exit 0;

fscanf ( fp, ”%d”, &razruad);

fclose ( fp );

    1. Работа с файлами на диске

Аналогичным образом, как это делается со стандартными потоками ввода–вывода, можно осуществлять работу с файлами на диске. Для этой цели в библиотеке языка СИ включены следующие функции:

fgetc( ) –ввод (чтение) одного символа из файла.

fputc( ) –запись одного символа в файл.

fprintf( ) –форматированный вывод в файл.

fscanf( ) –форматированный ввод (чтение) из файла.

fgets( ) –ввод (чтение) строки из файла. int fgets(FILE *stream);

fputs( ) –запись одной строки в файл.

1. int fgetc(FILE *stream ) –чтение одного символа из файла.

int fputc(int c, FILE *stream) – запись одного символа в файл.

Например:

void main(void)

{ FILE *stream;

char ch;

stream=fopen(“DYMMY.FIL”, “+w”);

do{ ch=fgetc(stream);

putch(ch); }

while (ch != EOF);

fclose(stream); }

Например:

#include<stdio.h>

void main(void)

{ char msg[]=“hello, world”;

int i=0;

while(msg[i])

fputc(msg[i], stdout); }

2. fprintf( ) –форматированный вывод в файл.

fscanf( ) –форматированный ввод (чтение) из файла.

Например:

#include<stdio.h>

void main(void)

{ FILE *stream;

int i=100;

float f= 1.2345;

char c=‘C’;

stream=fopen(“DYMMY.FIL”, “w+”);

fprintf(stream, “%d %c %.4f”, i, c, f);

fclose(stream); }

3. char *fgets(char *s, int n, FILE *stream) – функция чтения строки из файла.

int fputs(const char *s, FILE *stream) – функция записи строки в поток.

Например:

void main (void)

{FILE *stream;

char msg[20];

stream=fopen(“DYMMY.FIL”, “w+”);

fputs(“\nHello, WORLD”, stream);

fclose(stream);

stream=fopen(“DYMMY.FIL”, “r”);

fgets(msg, strlen(str)+1, stream);

printf(“%s”, msg);

fclose(stream); }

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

Программа ввода читает символы с клавиатуры и записывает их в файл.

Пусть признаком завершения ввода служит поступивший от клавиатуры символ “#”.

Имя файла запрашивается у пользователя.

Если при вводе последовательности символов была нажата клавиша <Enter>, служащая разделителем строк при вводе с клавиатуры, то в файл записываются коды “возврат каретки” (CR – значение 13) и “перевод строки” (LF – значение 10). Код CR в дальнейшем при вызове вызывает перевод маркера (курсора) в начало строки экрана дисплея. Код LF служит для перевода маркера на новую строку дисплея. Значения этих кодов в тексте программы обозначены соответственно идентификаторами CR и LF, т.е. CR и LF – именованные константы. Запись управляющих кодов CR и LF в файл позволяет при последующем вызове файла на экран отделить строки друг от друга.

Пример:

/*программа записи (ввода) символов в поток (файл)*/

#include<stdio.h>

int main() {

FILE *fp; /*указатель на поток*/

char c, fname[20]; /*массив для имени файла*/

const char CR=‘\015’, /*восьмиричный код возврата каретки*/

LF=‘\012’; /*восьмеричный код перевода строки*/

/*запрос имени файла*/

puts (“Введите имя файла:\n”);

gets(fname);

/*открыть файл для записи*\

if ((fp=fopen(fname, “w”)) = = NULL) {

perror(fname);

return 1; }

/*цикл ввода и записи в файл символов*\

while ((c=getch() ) != ‘#’) {

if ( c == ‘\n’) {

fputc(CR,fp);

fputc(LF,fp); }

else { fputc(c,fp);

putc(c); } }

/* цикл ввода завершен; закрыть поток: */

fclose (fp);

return 0; }

Пример:

/*программа вывода потока (файла) на экран дисплея*/

#include<stdio.h>

int main(void) {

FILE *fp; /*указатель на поток*/

char c;

char fname[20]; /*массив для имени файла*/

/*запрос имени файла*/

puts(“введите имя файла:\n”);

gets (fname);

/*открыть файл для чтения*/

if ( (fp=fopen(fname, “r”) ) == NULL)

{ perror(fname);

return 1; }

/*цикл чтения из файла и вывода символов на экран*/

while ( (c=fgetc(fp) ) != EOF )

putchar (c);

fclose(fp); } /*закрыть файл*/

ЗАКЛЮЧИТЕЛЬНЫЙ ПРИМЕР ПО ТЕМЕ "ФАЙЛЫ"

// Дан символьный файл f.dat. Записать в файл:

// h_otr.dat все отрицательные компоненты исходного файла,

// h_por.dat компоненты исходного файла, упорядоченные по возрастанию,

// h_sum.dat сумму, произведение и сумму квадратов компонент исходного файла.

# include <stdio.h>

void main ()

{ int a[100], // массив компонент исх. файла

perest, i, j, // вспомогательные переменные

kol, // количество компонент исх. файла

sum, pr, sum_2; // Сумма, произведение и сумма квадратов

FILE *fil, *f;

// Очищаем файлы h_otr.dat, и h_por.dat, и h_sum.dat

fil = fopen("h_otr.dat", "w");

fclose(fil);

fil = fopen("h_por.dat", "w");

fclose(fil);

fil = fopen( "h_sum.dat", "w" );

fclose (fil);

// Считываем из файла f.dat только отрицательные

// компоненты и заносим их в файл h_otr.dat

if ( ( fil = fopen ( "f.dat", "r" ) ) = = NULL)

{ puts( " Ошибка " );

exit (0); }

f = fopen( "h_otr.dat", "w" );

while ( fscanf ( fil, "%d", & kol ) != EOF )

if ( kol < 0) fprintf ( f, "%d\n", kol );

fclose ( fil );

fclose ( f );

// Упорядочиваем компоненты исходного файла

// по возрастанию заносим их в файл h_por.dat

if ( (

fil = fopen ( "f.dat", "r" )

) = = NULL)

{ puts( " Ошибка ");

exit (0); }

i = 0;

while ( ( a[i] = fgetc (fil) ) != EOF)

i ++;

fclose (fil);

kol = i;

for ( i = 0; i < kol; i++ ) {

for ( j = i; j < kol – 1; j++ ) {

if ( a[j] > a[j+1] ) {

perest = a[j];

a[j] = a[j+1];

a[j+1] = perest; } } }

fil = fopen( "h_por.dat", "w" );

for ( i = 0; i < kol; i++ )

fputc ( a[i], fil);

fclose (fil);

/* Находим сумму, произведение и сумму квадратов компонент файла f.dat, заносим их в файл h_sum.dat */

f = fopen( “h_sum.dat”, “w” );

sum = sum_2 = 0; pr = 1;

fil = fopen (“f.dat”, “r” );

for(i=0; ( fscanf(f, “%d”, &a[i])!=EOF); i++ ) ;

for(i=0; i<kol; i++ ) {

sum += a[i];

pr *= a[i];

sum_2 += a[i]*a[i]; }

fprintf(fil, “sum = %d \t pr = %d \t sum_2 = %d \n”, sum, pr, sum_2);

fclose (fil); }

71