Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КОНТРОЛЬНАЯ_РАБОТА_Лабораторный практикум Алгор...doc
Скачиваний:
2
Добавлен:
01.05.2025
Размер:
521 Кб
Скачать
☆

Лабораторная работа № 7

Дисциплина: Языки программирования

Тема: Файлы

Цель: знать все функции для работы с файлами, изучить все приведенные примеры и решить поставленные задач

Краткая теория

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

Функции ввода-вывода делятся на три класса:

ввод-вывод верхнего уровня;

ввод-вывод с консольного терминала путем непосредственного обращения к нему;

ввод-вывод нижнего уровня.

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

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

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

Когда программа начинает выполняться, автоматически открываются пять потоков. Эти потоки -стандартный BBOfl(stdin), стандартный вывод (stduot), стандартный вывод для сообщений об ошибках (stderr), стандартный последовательный порт (stdaux), стандартное устройство печати (stdpm).

По умолчанию стандартный ввод-вывод и стандартный вывод сообщений об ошибках связывается с консольным терминалом.

Когда поток открывается для ввода-вывода, он связывается со структурой типа FILE (имя типа FILE определяется с помощью конструкции typedef в файле stdio.h). Структура содержит разнообразную информацию о файле. При открытии файла определяется указатель на структуру типа FILE. Этот указатель -указатель потока - используется для последующих операций с файлом.

Функции открытия-закрытия

1. FILE * fopen(char * filename, char * type);

Функция открывает файл, имя которого задается аргументом filename, и связывает с ним поток (для выполнения высокоуровневого ввода-вывода).

Аргумент type указывает на строку символов, определяющих способ доступа к файлу, и может принимать следующие значения:

"г" открыть файл для чтения (файл должен существовать)

"w" открыть пустой файл для записи; если файл уже существует, то его содержимое теряется;

"а" открыть для записи в конец файла (добавления); файл создается если он не существует;

"r+" открыть для чтения и записи (файл должен существовать)

"w+" открыть пустой файл для чтения и записи; если файл существует, его содержимое теряется;

"а+" открыть для чтения и добавления; файл создается, если он не существует.

Для того, чтобы указать, что файл должен быть открыт в текстовом режиме, необходимо добавить символ Ч' в строку type, если в двоичном - Ъ'. Функция возвращает указатель на открытый поток или NULL, если обнаружена ошибка.

2. FILE * freopen(char * filename, char * type, FILE * stream);

Функция закрывает файл, связанный в текущий момент с потоком stream, и переназначает поток stream на файл, определенный в filename.

Функция обычно используется, чтобы связать потоки stdin, stduot, stderr,'stdaux, stdprn с файлами, задаваемыми пользователями.

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

3. int fclose(FILE * stream);

Функция закрывает все раннее открытые потоки, кроме стандартных и возвращает количество закрытых потоков или NULL, если произошла ошибка.

5. int feof(FILE * stream);

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

Возвращаемое значение:

0 – если не конец файла

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

Функции неформатированного ввода – вывода

  1. int fread (void * ptr, unsigned size, int count, FILE * stream);

Функция читает count элементов длины size байтов из входного потока stream и помещает по адресу ptr. Указатель потока смещается на число действительно прочитанных байтов. Символы ‘\n’, если они встречаются, специально не обрабатываются.

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

int write(void * ptr, unsigned size, int count, FILE *stream);

Функция считывает начиняя с адреса ptr не более чем количество count элементов размером size байт каждый, и записывает в выходной поток.

Форматных преобразований данных не производится. Символы ‘\n’ специальным образом не обрабатываются.

Функции ввода – вывода символов и строк

  1. int fgetc(FILE * stream);

int getc(FILE * stream);

Функции читают символ из входного потока stream с текущей позиции и перемещает внутренний указатель файла на следующий символ.

  1. int fgetchar(void);

int getchar(void);

Функции читают символ из стандартного входного потока stdin и приводят еого к типу int.

  1. int fputc(int sim, FILE * stream);

int putc(int sim, FILE * stream);

Функции записывают одиночный символ с кодом sim в поток streamв текущую позицию, и возвращает записанный символ или EOF, если втретился конец файла или ошибка.

  1. int fputchar(int sim);

int putchar(int sim);

Функции записывают символ в стандартный поток вывода и возвращают значение символа.

5. сһаг * fgets (сһаг * string, int n, ҒILE * stream);

Функция читает строку из входного потока stream и помещает ее в строку, адрес которой задается значением string.

Символы читаются из потока до тех пор, пока не будет прочитан символ новой строки ‘\n’, или пока не наступит конец потока или пока не будет прочитано n-1 символов.

Результат помещается в зггіп§ и заканчивается ‘\0’. Если n=1, то формируется пустая строка. Функция возвращает адрес строки.

6. int fputs(сһаг * string, FILE * stream);

Функция копирует строку из стандартного в поток stream, с текущей позиции. Завершающий символ ‘\0' не копируется. Возвращает последний записанный символ или ЕОF, если произошла ошибка.

7. сһаг * gets(сһаг * stream);

Функция читает строку из стандартного потока ввода strdin и помещает считанную строку по адресу string. В строку включаются все символы до первого '\n', не включая его. Строка результат заканчивается символом ‘\0’.

Возвращает указатель на результирующую строку.

8. int puts (сһаr * string);

Функция записывает строку string в стандартный поток вывода stbout, добавляя символ ‘\n’. Выводятся символы из строки, пока не встретится завершающий строку символ’\0’. Возвращает символ ‘\n' или ЕОF, если произошла ошибка.

Функции форматированного ввода-вывода

int fprint(FILE * stream, сһаг * format, <argument,...>);

Функция выполняет форматированные преобразования аргументов согласно строке формата format и выводит в поток stream.

int fprint (сһаг *s, сһаг * format, < argument,...>);

Функция выполняет форматированные преобразования аргументов согласно строке формата format и выводит в символьный массив, начиная с адреса s.

Для всех функций строка формата format аналогична строке формата для функции print. Все функции возвращают количество выведенных символов.

int fscan (FILE * stream, сһаг * format, < argument,...>);

Функция читает данные из входного потока зігеат, выполняет форматные преобразования и полученные значения записывает в переменные argument. Каждый из аргументов должен быть указателем на переменную типа, заданному очередным элементом описания поля формата.

int sscan ( сһаг * s, сһаr * format, < argument,...>);

Функция читает данные из символьного массива, адрес которого задается s, в переменные, адреса

которых задаются аргументами.

Для всех функций строка формата format аналогична строке формата для функции scanf. Все функции возвращают количество успешно введенных, преобразованных и присвоенных элементов ввода.

Примеры использования файлов

Когда программа открывает файл для ввода/вывода при помощи потоковых функций, открытый файл связывается с некоторой структурой типа FILE, содержащей базовую информацию об- этом файле. После открытия потока возвращается указатель на файловую структуру. Указатель файла, иногда называемый указателем потока, используется для ссылки к файлу при всех последующих операциях ввода/вывода.

Когда файл открыт, данные могут вноситься в него или считываться из него с использованием формата или без него. При необходимости форматного ввода/вывода (функции fprint и fscanf) используются стандартные строки формата, ранее рассматриваемые нами вместе с функциями print и scanf . Неформатированный ввод/вывод осуществляется функциями ігеаё и іжііе, который позволяет обмениваться данными указанного размера. Однако, при использовании обоих подходов, после выполнения функции, указатель потока смещается к следующему элементу файла. Таким способом можно перебрать все элементы, начиная с первого, до конца файла.

Рассмотрим способ последовательного перебора элементов файла.

1.

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

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

Эта программа объявляет потоковый указатель a. Функцией fopen этот указатель связывается с файлом ff, который будет открыт для записи. Долее, в файл вносятся элементы до тех пор, пока не будет введено значение нуля. Внесение элементов в файл осуществляется функцией форматированного выводы printf. По окончании программы файл закрывается функцией fclose, что означает очистку и закрытие связанного с данным потоком буфера.

#include<stdio.h>

main()

{

FILE * a;

Int I;

A=fopen(“ff”, “w”);

Scanf(“%d”, &i);

While(i!=0)

{

fprintf(a, “%d”,i);

scanf(“%d”, &i);

}

fclose(a);

}

Основная программа будет поддерживать связь с двумя потоками:

- потоком, связанным с исходным файлом ff, и открытым для записи в него элементов

FILE *a, *b;

…

a=fopen(“ff”, “r”);

b=fopen(“gg”, “w”);

Задача заключается в том, чтобы перебрать все элементы исходного файла. Поскольку нельзя сказать, сколько элементов находится в файле, то будем перебирать их по порядку, пока не кончится файл. Для этого воспользуемся функцией feof. Данная функция проверяет поток на признак конца файла:

- возвращает значение 1, если раннее была сделана попытка прочитать уже несуществующий в файле элемент

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

fscanf(a, “%d”, &i);

while (!feof(a))

{

if(i/100>=1 && i/100<=9)

fprintf(b, “%d”, i);

fscanf(a, “%d”, &i);

}

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

b=fopen(“gg”, “r”);

while(fscnf(b, “%d”, &i)!=EOF)

printf(“%d”/ i);

Вся программа будет выглядеть так:

include<stdio.h>

main()

FILE *a,*b;

int i;

a=fopen("ff","r");

b=fopen("gg","w");

fscanf(a,"%d",&i);

while (!feof(a))

{if(i/100>=l &&i/100<=9)

  • rprintf(b,"%d",i); fscanf(a,"%d",&i);

};

fclose(b);

b=fopen("gg","r"); while(fscanf(b,"%d",&i)!=EOF)

printf("%d",i); fcloseall()

2.

Дан файл, каждый элемент которого содержит сведения о фамилии, адресе и телефоне абонента.

Сформировать новый файл из тех элементов, которые содержат сведения об абонентах указанной телефонной станции.

Данная задача также распадается на две отдельных программы - программу формирования исходного файла и программу, выполняющую непосредственно условия задачи. Получаемые в результате выполнения таких программ файлы будут содержать элементы сложной формы - структуры. Ввод/вывод таких элементов невозможен посредством функций fscanf и fprintf, поэтому воспользуемся функциями неформатированного ввода/вывода.

Данные, обрабатываемые в задаче, определим через следующий структурный тип: struct abonent {

char name[20];

char adress[40];

char phone [8];

};

Объявим переменную такого типа:

struct abonent x; Заполняем поля переменной х сведениями об очередном абоненте:

printf("\n имя");

scanf("%s",x.name);

printf("\n адрес");

scanf("%s",x.adress);

printf("\n телефон");

scanf("%s",x.phone);

Затем заполненную структуру целиком записываем в файл через функцию fwrite:

fwrite(&x,sizeof(abonent), I ,a); Функция копирует, начиная с адреса х размером sizeof (abonent) байт, один элемент в поток а.

#include <stdio.h> main()

FILE * a;

struct abonent {

char name[20];

char adress[40];

char phone[8];

};

struct abonent x;

int n;

a=fopen("ff',"wn);

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

{

printf("\n имя");

scanf("%s",x.name);

printf("\n адрес");

scanf("%s",x.adress);

printf["\n телефон");

scanf("%s",x.phone);

fwrite(&x,sizeof(abonent),l,a);

}

fclose(a);

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

. #include <stdio.h>

main()

FILE *a,*b;

struct abonent {

char name [20];

charadress [40];

char phone [8];

};

struct abonent x; J-

char stantion;

printfl["\n введите номер станции");

scanf("%c",&stantion);

a=fopen("ff',"r");

b=foPen(“gg”, “w”);

fread(&x,sizeof(abonent), 1 ,a);

while (!feof(a))

{ if{x.phone[0]==stantion)

fwrite(&x,sizeof(abonent), 1 ,b);

fread(&x,sizeof(abonent), 1 ,a);

}

fclose(b);

b=fopen("gg","r");

fread(&x,sizeof(abonent), 1 ,b);

while (!feof(b))

{

printf("%s %s %s \n",x.name,x.adress, x.phone);

fread(&x,sizeof(abonent), I ,b);

} fcloseall();

}

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

Для перемещения указателя потока по файлу могут быть использованы следующие функции:

- fseek(FILE *a, long n, int k)

функция смещает указатель потока а на число n байтов :

либо относительно начала файла (k=0),

либо относительно текущего местоположения (k=l), либо относительно конца файла (k=2).

- rewind(FILE * а) функция перемещает указатель потока а на начало файла

Если необходимо определить текущее местоположение указателя потока относительно начала файла, можно воспользоваться функцией ftell. Такое значение определяется в байтах.

3.

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

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

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

a=fopen("ff',"r+");

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

В качестве максимального выбираем первый элемент файла. Очевидно, смещение его относительно начала файла равно нулю.

fread(&max,sizeof(int), I ,a);

nom=0;

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

while(!feof(a))

{ if(max<x)

{

max=x;

nom=(int)ftell(a)-sizeof(int);

}

fread(&x,2,l,a);

Осталось поменять местами два элемента:

rewind(a); - смещаемся к началу файла

fread(&x,2, 1 ,а); - запоминаем первый элемент в х

fseek(a,nom,0); - перемещаемся к месту максимума

fwrite(&x,2, 1 ,а); - пишем х на это место

rewind(a); - перемещаемся к началу файла

fwrite(&max,2, 1 ,a); - пишем максимум на первое место

Окончательный вариант программы :

^include <stdio.h> main()

FILE *a;

intmax, /*значение максимума */ nom, /*смещение максимума */

х;

a=fopen("ff","r+");

fread(&max,sizeof(int),1 ,a);

nom=0;

x=nom;

while(!feof(a))

{ if(max<x)

{

max=x;

nom=(int)ftell(a)-sizeof(int);

}

fread(&x,2,l,a);

}

rewind(a);

fread(&x,2,l,a);

fseek(a,nom,0);

fwrite(&x,2,l,a);

rewind(a);

fwrite(&max,2,1 ,a);

rewind(a);

fread(&x,2,l,a);

while(!feof(a))

{

printf("%d",i);

fread(&x,2,l,a);

}

fclose(a);

}