Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
LR_Fayly_2.doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
280.06 Кб
Скачать

Лабораторная работа № 1 Файлы

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

Теоретические сведения Файлы

Наиболее общее определение понятия файл в вычислительной технике и информатике можно дать следующим образом: файл – это поименованная совокупность данных, хранящаяся на внешнем носителе информации (внешнем запоминающем устройстве, не доступном, в отличие от основной памяти, для непосредственной обработки программой). Оно указывает на главное свойство файлов – сохранение своего состояния, пока не происходит их явная обработка той или иной программой, столь долго, сколько позволяют физические принципы, на которых организовано конкретное внешнее запоминающее устройство. Разумеется, это относится только к нормальным режимам работы устройства, сохранение состояния файла в аварийных режимах гарантировать невозможно. Приведенное определение включает в себя и более частные, например, файл – поименованная последовательность данных на внешнем носителе. Более того, данное определение можно применить и к таким часто рассматриваемым в вычислительной технике в качестве файлов объектам, как логические устройства посимвольного ввода и посимвольного вывода, если допустить, что это файлы, с которыми одновременно работают две программы: собственно программа и операционная система.

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

В языке программирования С под файлом или файловым потоком понимают средства доступа к данным на внешних запоминающих устройствах (файлах в выше приведенном определении, или «внешних файлах»). Часто также с файлами отождествляют структуры данных, используемые для доступа к конкретным внешним файлам.

Стандарт языка программирования С определяет набор функций, позволяющий получить доступ к внешним файлам через их имена, известные операционной системе, и в дальнейшем производить чтение и запись в такие файлы. Чтение и запись возможны как с применением форматирования (с преобразованием данных в символьное представление, по аналогии с форматированным вводом из стандартного потока ввода с помощью функции scanf() и форматированным выводом с помощью функции printf()), так и непосредственным копированием данных из внешнего файла в оперативную память и обратно.

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

Файловый указатель представляет собой указатель на структуру типа FILE, определенную в заголовочном файле <stdio.h> с помощью оператора typedef, содержащую информацию о файле: местонахождение файла с точки зрения операционной системы, текущую позицию в файле, характер операций, разрешенных при открытии файла, наличие ошибок и состояние признака конца файла, – и сведения о буферизации. Для файлового указателя также встречаются называния указатель на файловый поток, файловый поток, указатель на поток или просто поток. Это связано с одним из способов интерпретации внешних файлов как потока символов и показывает общность работы с файлами и со стандартными средствами ввода-вывода.

Объявление файлового указателя может быть как глобальным, так и локальным, внутри тела функции main() или какой-либо другой функции:

#include <stdio.h>

FILE *fp1;

int func1(double x);

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

{

FILE *fp2;

/* … Текст функции main() пропущен*/

}

int func1(double x)

{

FILE *fp3;

/* … Текст функции func1() пропущен*/

}

В приведенном примере объявляются три файловых указателя, соответственно fp1 в глобальной области видимости и fp2 и fp3 в локальных областях видимости функций main() и func1().

Открытие файла производится с помощью библиотечной функции fopen(), объявленной в заголовочном файле <stdio.h> следующим образом:

FILE *fopen(const char *filename, const char *mode);

Первый аргумент функции fopen() – символьная строка, содержащая имя файла. Это внешнее имя файла, записанное по правилам конкретной операционной системы, от следования этим правилам зависит возможность нахождения данного файла. При этом необходимо учитывать, что при задании данного имени как строкового литерала в двойных кавычках, происходит обычная трансляция последовательностей управляющих символов, начинающихся с обратной косой черты, и если она используется в имени файла (например, для разделения имен каталогов в полном имени файла в операционных системах MS-DOS и Windows), то ее необходимо представить в виде последовательности \\ (две косых черты подряд без каких либо иных символов между ними), такая последовательность будет рассматриваться компилятором как одна косая черта собственно в строковом литерале.

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

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

  • w (от write) – режим открытия для записи; данная (открывающая) программа, запущенная некоторым конкретным пользователем, должна иметь право вести запись в данный файл (определяется операционной системой). Отсутствие права на запись в данный файл является ошибкой. Если открываемый файл существует, то все его содержимое стирается, а его длина становится равной 0. Если открываемый файл не существует, то предпринимается попытка его создания, для чего данная (открывающая) программа, запущенная некоторым конкретным пользователем, должна иметь право создания файла с указанным именем (определяется операционной системой – например, может требоваться отдельный допуск для записи в указанный каталог или существовать отдельный допуск для создания файлов вообще). Отсутствие права на создание файла с указанным именем является ошибкой.

  • a (от append) – режим открытия для добавления в конец файла; данная (открывающая) программа, запущенная некоторым конкретным пользователем, должна иметь право на запись в данный файл (определяется операционной системой). Отсутствие права на запись в данный файл является ошибкой. Если открываемый файл существует, то все его содержимое сохраняется, добавление новых данных производится сразу после существующих, размер файла увеличится на количество записанных байт. Если открываемый файл не существует, то предпринимается попытка его создания. Отсутствие права на создание файла с указанным именем является ошибкой.

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

  • r+”  – режим открытия для модифицирования (чтения и записи) существующего файла.

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

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

В некоторых системах (например, MS-DOS и совместимых с ними, ОС семейства Microsoft Windows) делается различие между текстовыми и двоичными файлами, также называемыми «бинарными файлами» от английского «binary» – «двоичный». Для текстовых файлов производится преобразование некоторых символов при записи в файл в другие символы или последовательности символов и обратное преобразование при чтении из файла (фактически в файл записывается байт или последовательность байт, соответствующая кодам символов). Для двоичных файлов никаких преобразований не производится, каждый символ однозначно записывается в файл своим кодом и именно записанный в данную позицию в файле байт будет впоследствии из нее прочитан. В других системах (например, FreeBSD, OpenBSD и ОС семейства Linux), такого различия нет, поскольку и для текстовых файлов никаких преобразований не производится. Для систем, делающих различие между текстовыми и двоичными файлами, при работе с последними необходимо добавить в конец строки режима букву ”b” (от ”binary”). Тогда возможные строки режима для них будут выглядеть как rb”, wb”, ab”, r+b”, w+b”, a+b”. Согласно стандарту языка С, для указания открытия файла как текстового специального модификатора в строке режима не предусмотрено. Однако многие компиляторы допускают указания в строке режима сразу после одного (r, w, a) или двух (r+”, w+”, a+”) символов, специфицирующих разрешенные операции для работы с файлом, произвольной строки, и интерпретируют ее следующим образом: если она начинается с символа ”b” – то файл рассматривается как двоичный, иначе как текстовый. Поэтому, для таких компиляторов часто встречающиеся в литературе строки режима открытия файла как текстового rt, wt, at, r+t, w+t, a+t не являются ошибкой и приводят к ожидаемому результату, однако в стандарте языка C89 подобное поведение не определено и использования таких строк следует, по возможности, избегать.

В случае если файл открыть не удается, функция fopen() возвращает NULL. Поэтому перед работой с файлом через файловый указатель необходимо проверить успешность открытия файла, например следующим образом:

fp1 = fopen(name, mode);

if(fp1 != NULL)

{

/* Работаем с успешно открытым файлом*/

}

Программа на языке С при запуске на выполнение автоматически открывает три файла и создает для них файловые указатели. Это стандартный поток ввода, стандартный поток вывода и стандартный поток ошибок, предоставляемые операционной системой. Соответствующие им файловые указатели (объекты типа FILE*) объявлены в заголовочном файле <stdio.h> и называются stdin, stdout и stderr. Следует обратить внимание, что файловые указатели stdin, stdout и stderr являются константами, а не переменными, поэтому им нельзя присваивать какие-либо значения.

Для разрыва связи, установленной функцией fopen() между внешним файлом и файловым указателем, служит функция fclose():

int fclose(FILE *fp);

Функция fclose() выполняет запись буферизованных, но еще не записанных физически во внешний файл данных, уничтожает в памяти (но не в самом внешнем файле!) буферизованные входные данные, еще не прочитанные программой, освобождает все автоматически выделенные буферы, после чего закрывает файловый указатель и его можно повторно использовать для других файлов. Функция fclose() возвращает значение EOF (это константа типа int, описанная в заголовочном файле <stdio.h>, используется рядом функций для указания достижения конца файла или как признак ошибки) в случае ошибки и нуль в противном случае. Для гарантии корректного закрытия файлов и сохранения данных, находящихся в буфере, но еще на записанных на внешнее запоминающее устройство, следует принудительно вызывать функцию fclose() для всех открытых и еще не закрытых файлов при завершении работы программы. Во время работы программы использование функции fclose() рекомендуется для закрытия более не используемых файлов, чтобы не превысить максимальное число открытых одной программой файлов, определяемое операционной системой. Следует обратить внимание, что значение самого файлового указателя при закрытии файла не изменяется, но использовать его для доступа к файлам нельзя (структура, на которую он указывает, более не содержит корректных данных для доступа к внешнему файлу).

Функция fclose() может использоваться и для разрыва связи файловых указателей stdin, stdout и stderr с соответствующими файлами, по умолчанию связанными с вводом с клавиатуры и выводом на экран. После разрыва данных связей, файловые указатели stdin, stdout и stderr могут быть переназначены на другие внешние файлы с помощью функции freopen():

FILE *freopen(const char *filename,

const char *mode, FILE *stream);

Функция freopen() открывает файл в заданном режиме (параметры filename и mode соответственно) и ассоциирует с ним поток stream. Параметр stream должен быть действительным указателем на структуру типа FILE, соответствующую либо открытому файлу, либо ранее успешно открытому и впоследствии закрытому файлу (поскольку функция freopen(), в отличие от fopen(), сама память не распределяет). Возвращает указатель на поток (значение fstream) или NULL.

Для записи всех буферизованных, но еще не записанных данных в поток вывода (файл, открытый для записи, добавления или модификации) может быть применена функция fflush():

int fflush(FILE *stream)

В случае ошибки функция возвращает EOF, в противном случае – нуль. Результат применения к потоку ввода не определен. Вызов fflush(NULL) выполняет указанные операции для всех потоков вывода.

Для более детального управления буферизацией служат функции setbuf() и setvbuf(), описание которых приводится в специальной литературе [Керниган, Ричи 2013, Б.1.1]

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]