Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР17-С++03-сентября-2012.doc
Скачиваний:
17
Добавлен:
08.11.2019
Размер:
698.88 Кб
Скачать

1.3. Основные методы обработки текстовых файлов

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

Рассмотрим последовательность действий по созданию простого текстового файла на языке C и записи в него текстовой информации.

Пример 17.1. Запись текстовой информации в файл.-Работает

#include <stdio.h>

#include <iostream.h>

#include <conio.h>

int main()

{

char str_file[]="String for file"; // Строка для файла

FILE* fp = fopen("prim17-1.txt","w");

if(fp != NULL)

{

printf("Идет запись информации в файл…\n");

for(int i=0;i < strlen(str_file);i++)

putc(str_file[i],fp);

}

else printf("Невозможно открыть файл на запись.\n");

fclose(fp);

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

В данном примере задается специализированный указатель fp типа FILE, который инициализируется функцией fopen(). Функция fopen() в качестве первого аргумента принимает строку, в которой задан путь и имя файла. Вторым параметром определяется способ обработки файла, в данном случае, значение "w", которое означает открытие файла на запись с удалением всей прежней информации из него. Если файл открыт успешно, то указатель fp не будет равен NULL и с ним возможна работа. В этом случае с помощью функции putc() выполняется запись символов в файл, на который указывает указатель fp. Перед завершением программы открытый файл следует закрыть во избежание в нем потери данных. Это достигается функцией fclose(), которая принимает указатель на файл и возвращает значение 0 при успешном закрытии файла, иначе значение EOF.

Замечание: строка "String for file" будет записана в файл с именем "prim17-1.txt" в текущий каталог (где храниться исполняемая программа); откройте указанный файл с помощью соответствующего редактора текста и убедитесь в наличии текста в этом файле.

Рассмотрим теперь пример программы считывания информации из файла.

Пример 17.2. Считывание текстовой информации из файла.-Работает

#include <stdio.h>

#include <iostream.h>

#include <conio.h>

int main()

{

char str_file[]="String for file"; // Строка для файла

FILE* fp = fopen("prim17-1.txt","w");

if(fp != NULL)

{

printf("Идет запись информации в файл…\n");

for(int i=0;i < strlen(str_file);i++)

putc(str_file[i],fp);

}

else printf("Невозможно открыть файл на запись.\n");

fclose(fp);

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

Замечание: на экран будет выведена строка "String for file", хранящаяся в файле с именем "prim17-1.txt", сформированном в предыдущем примере (программе).

В приведенном примере функция fopen() открывает файл на чтение, что определяется значением второго аргумента равного «r». Это значит, что в него невозможно произвести запись данных, а только считывание. Сначала выполняется цикл while, в котором из файла считывается символ с помощью функции getc() и выполняется проверка: если считанное значение не равно символу конца файла EOF, то значение переменной ch записывается в массив strfile. Данный цикл будет выполняться до тех пор, пока не будут считаны все символы из файла, т.е. пока не будет достигнут символ EOF. После завершения цикла формируется строка strfile, которая выводится на экран с помощью функции printf(). Перед завершением программы также выполняется функция закрытия файла fclose().

Пример 17.3. Чтение одного элемента из файла, обработка и запись результата в текстовый файл.-Работает

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <iostream.h>

int main()

{ FILE *f;

int dig;

if ((f = fopen("prim17-3.txt","r")) == NULL) // открыть файл для чтения

{ printf("Невозможно открыть файл!\n");

exit(0);

}

fscanf(f, "%d", &dig); // считать значение dig из файла

fclose(f); // закрыть файл

f = fopen("prim17-3-1.txt","w"); // открыть файл для записи

fprintf(f, "We have read the number %d", dig);

fclose(f); // закрыть файл

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

Мы использовали один и тот же указатель на файловый поток дважды, так как прежде, чем обращаться к нему вторично, закрыли связанный с ним файл и освободили, таким образом, указатель.

Примечание к примеру 17.3:

– исходное число в файле prim17-3.txt : 2345

– сформированный текст в файле prim17-3-1.txt : We have read the number 2345

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

Пример 17.4. Написать программу, которая сжимает содержимое файла, записывая в выходной файл лишь каждый третий символ из входного файла. -Работает

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <string.h>

#include <iostream.h>

int main(int argc, char *argv[])

{

FILE *f_in = fopen("prim17-4.txt","r"); // имя входного файла

FILE *f_out = fopen("prim17-4-1.txt","w"); // имя выходного файла

int ch;

int count = 0; // счетчик элементов

if (f_in != NULL)

{

while((ch = fgetc(f_in)) != EOF)

if (count++ % 3 == 0)

fputc(ch, f_out); // выводит каждый третий символ

fclose(f_in);

fclose(f_out);

}

else printf("Невозможно открыть файл\n ");

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

Примечание к примеру 17.4:

– исходный текст в файле prim17-4.txt : String for file

– сформированный текст в файле prim17-4-1.txt : Si ri

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

Пример 17.5. Построчное чтение информации из входного файла и вывод ее на экран как на стандартное устройство вывода. Не работает !!!!!

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <string.h>

void main(int argc, char *argv[])

{ FILE *f_in;

char buffer[256]; // максимальная длина строки - 255 символов

char *name; // имя входного файла

if (argc<2) // в командной строке нет имени

{ printf("Введите имя входного файла");

gets(name);

}

else name = argv[1]; // взять имя из командной строки

if ((f_in = fopen(name, "r")) != NULL)

{ while (fgets(buffer,255,f_in)) != NULL)

{ fputs(buffer, stdout);

fputc('\n', stdout);

}

fclose(f_in);

}

else printf("Невозможно открыть файл\n ");

}

В цикле while присутствуют две файловые функции работы со строками: fgets() для чтения строки символов в буфер и fputs() - для записи содержимого буфера в файл.

Закрыть файл не менее важно, чем открыть его, так как в этот момент происходит заполнение ячейки таблицы размещения файлов значением, которое является признаком завершения файла и установка атрибутов файла.

Кроме того, вывод текстового файла буферизован. Это значит, что в тот момент, когда работает оператор записи в файл, фактическая запись может и не происходить, поскольку реально сначала происходит заполнение данными текстового буфера, а потом его содержимое записывается на диск. Запись буфера происходит как только он окажется полностью заполненным или при выполнении специальных команд принудительной записи на диск. Процесс записи недозаполненного буфера на диск называется флэшированием и обычно выполняется с помощью функции fflush(f_out). При необходимости завершить работу сразу со всеми открытыми файлами пользуются функцией flushall().

Закрытие файла посредством функции fclose(f_out) также включает процесс флэширования, то есть перенос информации из буфера на диск.

Доступ к элементам текстовых файлов возможен только в последовательном режиме как при записи файла, так и при его чтении.

Работа с текстовыми файлами через функции putc и getc не всегда удобна. Например, если необходимо записать или считать строку целиком, то желательно иметь функции, выполняющие эту работу. В качестве таковых можно воспользоваться функциями fputs() и fgets() для работы со строками. Перепишем предыдущие примеры 17.1 и 17.2 с использованием данных функций.

Пример 17.6. Использование функций fpust() и fgets().-Работает

#include <stdio.h>

#include <iostream.h>

#include <conio.h>

int main()

{

char str_file[]=”String for file”;

FILE* fp = fopen(“prim17-6.txt”,”w”);

if(fp != NULL) fputs(str_file,fp);

else printf(“Невозможно открыть файл на запись.\n”);

fclose(fp);

fp = fopen(“prim17-6.txt”,”r”);

if(fp != NULL)

{

fgets(str_file,sizeof(str_file),fp);

printf(str_file);

}

fclose(fp);

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

Аналогичные действия по записи данных в файл и считывания информации из него можно выполнить и с помощью функций fprintf() и fscanf(). Однако эти функции предоставляют большую гибкость в обработке данных файла. Продемонстрируем это на следующем примере. Допустим, имеется структура, хранящая информацию о книге: название, автор, год издания. Необходимо написать программу сохранения этой информации в текстовый файл и их считывания. Использование данных функций представлено в примере 17.7.

Пример 17.7. Использование функций fprintf() и fscanf().-Работает

#include <stdio.h>

#include <iostream.h>

#include <conio.h>

#define N 2

struct tag_book

{

char name[100];

char author[100];

int year;

} books[N];

int main()

{

for(int i=0;i < N;i++)

{

scanf("%s",books[i].name);

scanf("%s",books[i].author);

scanf("%d",&books[i].year);

}

for(int i=0;i < N;i++)

{

puts(books[i].name);

puts(books[i].author);

printf("%d\n",books[i].year);

}

FILE* fp = fopen("prim17-7.txt","w");

for(int i=0;i < N;i++)

fprintf(fp,"%s %s %d\n",books[i].name,books[i].author,books[i].year);

fclose(fp);

fp = fopen("prim17-7.txt","r");

for(int i=0;i < N;i++)

fscanf(fp,"%s %s %d\n",books[i].name,books[i].author,&books[i].year);

fclose(fp);

printf("------------------------------------------------\n");

for(int i=0;i < N;i++)

{

puts(books[i].name);

puts(books[i].author);

printf("%d\n",books[i].year);

}

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

При выполнении данной программы вводится информация по книгам в массив структур books и выводится введенная информация на экран. Затем открывается файл prim17-7.txt на запись, в который заносится информация по книгам в порядке: наименование, автор, год издания. Так как число книг в данном случае равно двум, то выходной файл будет выглядеть следующим образом:

Onegin Pushkin 1983

Oblomov Griboedov 1985

Затем, файл prim17-7.txt открывается на чтение и с помощью функции scanf() осуществляется считывание информации в элементы структуры. В заключении считанная информация выводится на экран монитора.

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

При внимательном рассмотрении предыдущих примеров можно заметить, что функции считывания информации из файла «знают» с какой позиции следует считывать очередную порцию данных. Действительно, в последнем примере функция fscanf(), вызываемая в цикле, «знает» что нужно считать сначала первую строку из файла, затем вторую и т.д. И программисту нет необходимости задавать позицию для считывания данных. Все происходит автоматически. Вследствие чего появляется такая особенность работы? Дело в том, что у любого открытого файла в программе написанной на С имеется указатель позиции (номера), с которой осуществляется считывание данных из файла. При открывании файла на чтение номер этой позиции указывает на начало файла. Поэтому функция fscanf(), вызванная первый раз, считывает данные первой строки. По мере считывания информации из файла, позиция сдвигается на число считанных символов. И функция fscanf() вызванная второй раз будет работать уже со второй строкой в файле. Несмотря на то, что указатель позиции в файле перемещается автоматически, в языке С имеются функции fseek() и ftell(), позволяющие программно управлять положением позиции в файле. Синтаксис данных функций следующий:

int fseek( FILE *stream, long offset, int origin);

long ftell(FILE *stream);

где *stream - указатель на файл; offset - смещение позиции в файле (в байтах); origin - флаг начального отсчета, который может принимать значения: SEEKEND - конец файла, SEEKSET - начало файла; SEEKCUR - текущая позиция. Последняя функция возвращает номер текущей позиции в файле.

Рассмотрим действие данных функций на примере считывания символов из файла в обратном порядке.

Пример 17.8. Использование функций fseek() и ftell().-Работает

#include <stdio.h>

#include <iostream.h>

#include <conio.h>

int main()

{

FILE* fp = fopen("prim17-8.txt","w");

if(fp != NULL)

{

fprintf(fp,"It is an example using fseek and ftell functions.");

}

fclose(fp);

fp = fopen("prim17-8.txt","r");

if(fp != NULL)

{

char ch;

fseek(fp,0L,SEEK_END);

long length = ftell(fp);

printf("length = %ld\n",length);

for(int i = 1;i <= length;i++)

{

fseek(fp,-i,SEEK_END);

ch = getc(fp);

putchar(ch);

}

}

fclose(fp);

cout << "\nНажмите любую клавишу..." ;

getch();

return 0;

}

В данном примере сначала создается файл, в который записывается строка "It is an example using fseek and ftell functions.". Затем этот файл открывается на чтение и с помощью функции fseek(fp,0L,SEEK_END) указатель позиции помещается в конец файла. Это достигается за счет установки флага SEEK_END, который перемещает позицию в конец файла при нулевом смещении. В результате функция ftell(fp) возвратит число символов в открытом файле. В цикле функция fseek(fp,-i,SEEK_END) смещает указатель позиции на -i символов относительно конца файла, после чего считывается символ функцией getc(), стоящий на i-й позиции с конца. Так как переменная i пробегает значения от 1 до length, то на экран будут выведены символы из файла в обратном порядке.