Определение текущей позиции в файле ее установка си
Стандартная библиотека Си содержит набор функций для работы с файлами. Эти функции описаны в стандарте ANSI . Отметим, что файловый ввод-вывод не является частью языка Си , и ANSI -функции - не единственное средство ввода-вывода. Так, в операционной системе Unix более популярен другой набор функций ввода-вывода, который можно использовать не только для работы с файлами, но и для обмена по сети. В C++ часто используются библиотеки классов для ввода-вывода. Тем не менее, функции ANSI -библиотеки поддерживаются всеми Си -компиляторами, и потому программы, применяющие их, легко переносятся с одной платформы на другую. Прототипы функций ввода-вывода и используемые для этого типы данных описаны в стандартном заголовочном файле "stdio.h.
Открытие файла: функция fopen
Для доступа к файлу применяется тип данных FILE . Это структурный тип, имя которого задано с помощью оператора typedef в стандартном заголовочном файле "stdio.h". Программисту не нужно знать, как устроена структура типа файл: ее устройство может быть системно зависимым, поэтому в целях переносимости программ обращаться явно к полям струтуры FILE запрещено. Тип данных "указатель на структуру FILE используется в программах как черный ящик: функция открытия файла возвращает этот указатель в случае успеха, и в дальнейшем все файловые функции применяют его для доступа к файлу.
Прототип функции открытия файла выглядит следующим образом:
Здесь path - путь к файлу (например, имя файла или абсолютный путь к файлу), mode - режим открытия файла. Строка mode может содержать несколько букв. Буква " r " (от слова read) означает, что файл открывается для чтения (файл должен существовать). Буква " w " (от слова write) означает запись в файл, при этом старое содержимое файла теряется, а в случае отсутствия файла он создается. Буква " a " (от слова append) означает запись в конец существующего файла или создание нового файла, если файл не существует.
В некоторых операционных системах имеются различия в работе с текстовыми и бинарными файлами (к таким системам относятся MS DOS и MS Windows; в системе Unix различий между текстовыми и бинарными файлами нет). В таких системах при открытии бинарного файла к строке mode следует добавлять букву " b " (от слова binary), а при открытии текстового файла -- букву " t " (от слова text). Кроме того, при открытии можно разрешить выполнять как операции чтения, так и записи; для этого используется символ + (плюс). Порядок букв в строке mode следующий: сначала идет одна из букв " r ", " w ", " a ", затем в произвольном порядке могут идти символы " b ", " t ", " + ". Буквы " b " и " t " можно использовать, даже если в операционной системе нет различий между бинарными и текстовыми файлами, в этом случае они просто игнорируются.
Значения символов в строке mode сведены в следующую таблицу:
Несколько примеров открытия файлов:
Обратите внимание, что во втором случае мы используем обычную косую черту / для разделения директорий, хотя в системах MS DOS и MS Windows для этого принято использовать обратную косую черту \ . Дело в том, что в операционной системе Unix и в языке Си, который является для нее родным, символ \ используется в качестве экранирующего символа, т.е. для защиты следующего за ним символа от интерпретации как специального. Поэтому во всех строковых константах Си обратную косую черту надо повторять дважды, как это и сделано в третьем примере. Впрочем, стандартная библиотека Си позволяет в именах файлов использовать нормальную косую черту вместо обратной; эта возможность была использована во втором примере.
В случае удачи функция fopen открытия файла возвращает ненулевой указатель на структуру типа FILE , описывающую параметры открытого файла. Этот указатель надо затем использовать во всех файловых операциях. В случае неудачи (например, при попытке открыть на чтение несуществующий файл) возвращается нулевой указатель. При этом глобальная системная переменная errno, описанная в стандартном заголовочном файле "errno.h, содержит численный код ошибки. В случае неудачи при открытии файла этот код можно распечатать, чтобы получить дополнительную информацию:
Константа NULL
В приведенном выше примере при открытии файла функция fopen в случае ошибки возвращает нулевой указатель на структуру FILE . Чтобы проверить, произошла ли ошибка, следует сравнить возвращенное значение с нулевым указателем. Для наглядности стандартный заголовочный файл "stdio.h" определяет символическую константу NULL как нулевой указатель на тип void :
Сделано это вроде бы с благой целью: чтобы отличить число ноль от нулевого указателя. При этом язык Си, в котором контроль ошибок осуществляется недостаточно строго, позволяет сравнивать указатель общего типа void * с любым другим указателем. Между тем,в Си вместо константы NULL всегда можно использовать просто 0 , и вряд ли от этого программа становится менее понятной. Более строгий язык C++ запрещает сравнение разных указателей, поэтому в случае C++ стандартный заголовочный файл определяет константу NULL как обычный ноль:
Автор языка C++ Б. Страуструп советует использовать обычный ноль 0 вместо символического обозначения NULL . Тем не менее, по традиции большинство программистов любят константу NULL .
Константа NULL не является частью языка Си или C++, и без подключения одного из стандартных заголовочных файлов, в котором она определяется, использовать ее нельзя. (По этой причине авторы языка Java добавили в язык ключевое слово null , записываемое строчными буквами.) Так что в случае Си или C++ безопаснее следовать совету Б. Страуструпа и использовать обычный ноль 0 вместо символической константы NULL .
Диагностика ошибок: функция perror
Функции бинарного чтения и записи fread и fwrite
После того как файл открыт, можно читать информацию из файла или записывать информацию в файл. Рассмотрим сначала функции бинарного чтения и записи fread и fwrite . Они называются бинарными потому, что не выполняют никакого преобразования информации при вводе или выводе (с одним небольшим исключением при работе с текстовыми файлами, которое будет рассмотрено ниже): информация хранится в файле как последовательность байтов ровно в том виде, в котором она хранится в памяти компьютера.
Функция чтения fread имеет следующий прототип:
Здесь size_t определен как беззнаковый целый тип в системных заголовочных файлах. Функция пытается прочесть numElems элементов из файла, который задается указателем f на структуру FILE , размер каждого элемента равен elemSize . Функция возвращает реальное число прочитанных элементов, которое может быть меньше, чем numElems , в случае конца файла или ошибки чтения. Указатель f должен быть возвращен функцией fopen в результате успешного открытия файла. Пример использования функции fread :
В этом примере файл " tmp.dat " открывается на чтение как бинарный, из него читается 100 вещественных чисел размером 8 байт каждое. Функция fread возвращает реальное количество прочитанных чисел, которое меньше или равно, чем 100.
Функция fread читает информацию в виде потока байтов и в неизменном виде помещает ее в память. Следует различать текстовое представление чисел и их бинарное представление! В приведенном выше примере числа в файле должны быть записаны в бинарном виде, а не в виде текста. Для текстового ввода чисел надо использовать функции ввода по формату, которые будут рассмотрены ниже.
Внимание! Открытие файла как текстового с помощью функции fopen , например,
вовсе не означает, что числа при вводе с помощью функции fopen будут преобразовываться из текстовой формы в бинарную! Из этого следует только то, что в операционных системах, в которых строки текстовых файлов разделяются парами символами " \r\n " (они имеют названия CR и LF - возврат каретки и продергивание бумаги, Carriage Return и Line Feed ), при вводе такие пары символов заменяются на один символ " \n " (продергивание бумаги). Обратно, при выводе символ " \n " заменяется на пару " \r\n ". Такими операционными системами являются MS DOS и MS Windows. В системе Unix строки разделяются одним символом " \n " (отсюда проистекает обозначение " \n ", которое расшифровывается как new line). Таким образом, внутреннее представление текста всегда соответствует системе Unix, а внешнее - реально используемой операционной системе. Отметим также, что создатели операционной системы компьютеров Apple Macintosh выбрали, чтобы жизнь не казалась скучной, третий, отличный от двух предыдущих, вариант: текстовые строки разделяются одним символом " \r " возврат каретки!
Такое представление текстовых файлов восходит к тем уже далеким временам, когда еще не было компьютерных мониторов и для просмотра текста использовались электрифицированные пишущие машинки или посимвольные принтеры. Текстовый файл фактически представлял собой программу печати на пишущей машинке и, таким образом, содержал команды возврата каретки и продергивания бумаги в конце каждой строки.
Функция бинарной записи в файл fwrite аналогична функции чтения fread . Она имеет следующий прототип:
Функция возвращает число реально записанных элементов, которое может быть меньше, чем numElems , если при записи произошла ошибка - например, не хватило свободного пространства на диске. Пример использования функции fwrite :
Закрытие файла: функция fclose
По окончании работы с файлом его надо обязательно закрыть. Система обычно запрещает полный доступ к файлу до тех пор, пока он не закрыт. (Например, в нормальном режиме система запрещает одновременную запись в файл для двух разных программ.) Кроме того, информация реально записывается полностью в файл лишь в момент его закрытия. До этого она может содержаться в оперативной памяти (в так называемой файловой кеш -памяти), что при выполнении многочисленных операций записи и чтения значительно ускоряет работу программы.
Для закрытия файла используется функция fclose с прототипом
В случае успеха функция fclose возвращает ноль, при ошибке -- отрицательное значение (точнее, константу конец файла EOF, определенную в системных заголовочных файлах как минус единица). При ошибке можно воспользоваться функцией perror , чтобы напечатать причину ошибки. Отметим, что ошибка при закрытии файла - явление очень редкое (чего не скажешь в отношении открытия файла), так что анализировать значение, возвращаемое функцией fclose , в общем-то, не обязательно. Пример использования функции fclose :
Пример: подсчет числа символов и строк в текстовом файле
В качестве содержательного примера использования рассмотренных выше функций файлового ввода приведем программу, которая подсчитывает число символов и строк в текстовом файле. Программа сначала вводит имя файла с клавиатуры. Для этого используется функция scanf ввода по формату из входного потока, для ввода строки применяется формат " %s . Затем файл открывается на чтение как бинарный (это означает, что при чтении не будет происходить никакого преобразования разделителей строк). Используя в цикле функцию чтения fread , мы считываем содержимое файла порциями по 512 байтов, каждый раз увеличивая суммарное число прочитанных символов. После чтения очередной порции сканируется массив прочитанных символов и подсчитывается число символов " \n " продергивания бумаги, которые записаны в концах строк текстовых файлов как в системе Unix, так и в MS DOS или MS Windows. В конце закрывается файл и печатается результат.
Пример выполнения программы: она применяется к собственному тексту, записанному в файле "wc.cpp.
Форматный ввод-вывод: функции fscanf и fprintf
В отличие от функции бинарного ввода fread , которая вводит байты из файла без всякого преобразования непосредственно в память компьютера, функция форматного ввода fscanf предназначена для ввода информации с преобразованием ее из текстового представления в бинарное. Пусть информация записана в текстовом файле в привычном для человека виде (т.е. так, что ее можно прочитать или ввести в файл, используя текстовый редактор). Функция fscanf читает информацию из текстового файла и преобразует ее во внутреннее представление данных в памяти компьютера. Информация о количестве читаемых элементов, их типах и особенностях представления задается с помощью формата. В случае функции ввода формат - это строка, содержащая описания одного или нескольких вводимых элементов. Форматы, используемые функцией fscanf , аналогичны применяемым функцией scanf , они уже неоднократно рассматривались (см. раздел 3.5.4). Каждый элемент формата начинается с символа процента " % ". Наиболее часто используемые при вводе форматы приведены в таблице:
Прототип функции fscanf выглядит следующим образом:
Многоточие здесь означает, что функция имеет переменное число аргументов, большее двух, и что количество и типы аргументов, начиная с третьего, произвольны. На самом деле, фактические аргументы, начиная с третьего, должны быть указателями на вводимые переменные. Несколько примеров использования функции fscanf :
Функция fscanf возвращает число успешно введенных элементов. Таким образом, возвращаемое значение всегда меньше или равно количеству процентов внутри форматной строки (которое равно числу фактических аргументов минус 2).
Функция fprintf используется для форматного вывода в файл. Данные при выводе преобразуются в их текстовое представление в соответствии с форматной строкой. Ее отличие от форматной строки, используемой в функции ввода fscanf , заключается в том, что она может содержать не только форматы для преобразования данных, но и обычные символы, которые записываются без преобразования в файл. Форматы, как и в случае функции fscanf , начинаются с символа процента " % ". Они аналогичны форматам, используемым функцией fscanf . Небольшое отличие заключается в том, что форматы функции fprintf позволяют также управлять представлением данных, например, указывать количество позиций, отводимых под запись числа, или количество цифр после десятичной точки при выводе вещественного числа. Некоторые типичные примеры форматов для вывода приведены в следующей таблице:
Прототип функции fprintf выглядит следующим образом:
int fprintf(FILE *f, const char *format, . );
Многоточие, как и в случае функции fscanf , означает, что функция имеет переменное число аргументов. Количество и типы аргументов, начиная с третьего, должны соответствовать форматной строке. В отличие от функции fscanf , фактические аргументы, начиная с третьего, представляют собой выводимые значения, а не указатели на переменные. Для примера рассмотрим небольшую программу, выводящую данные в файл "tmp.dat":
В результате выполнения этой программы в файл "tmp.dat" будет записан следующий текст:
В последнем примере форматная строка содержит внутри себя двойные апострофы. Это специальные символы, выполняющие роль ограничителей строки, поэтому внутри строки их надо экранировать (т.е. защищать от интерпретации как специальных символов) с помощью обратной косой черты \ , которая, напомним, в системе Unix и в языке Си выполняет роль защитного символа. Отметим также, что мы воспользовались стандартной функцией sqrt , вычисляющей квадратный корень числа, и стандартной функцией strlen , вычисляющей длину строки.
Понятие потока ввода или вывода
В операционной системе Unix и в других системах, использующих идеи системы Unix (например, MS DOS и MS Windows), применяется понятие потока ввода или вывода. Поток представляет собой последовательность байтов. Различают потоки ввода и вывода. Программа может читать данные из потока ввода и выводить данные в поток вывода. Программы можно запускать в конвейере, когда поток вывода первой программы является потоком ввода второй программы и т.д. Для запуска двух программ в конвейере используется символ вертикальной черты | между именами программ в командной строке. Например, командная строка
означает, что поток вывода программы ab направляется на вход программе cd , а поток вывода программы cd - на вход программе ef . По умолчанию, потоком ввода для программы является клавиатура, поток вывода назначен на терминал (или, как говорят программисты, на консоль). Потоки можно перенаправлять в файл или из файла, используя символы больше > и меньше < , которые можно представлять как воронки. Например, командная строка
перенаправляет выходной поток программы abcd в файл "tmp.res", т.е. данные будут выводиться в файл вместо печати на экране терминала. Соответственно, командная строка
заставляет программу abcd читать исходные данные из файла "tmp.dat" вместо ввода с клавиатуры. Командная строка
перенаправляет как входной, так и выходной потоки: входной назначается на файл "tmp.dat", выходной -- на файл "tmp.res".
В Си работа с потоком не отличается от работы с файлом. Доступ к потоку осуществляется с помощью переменной типа FILE * . В момент начала работы Си-программы открыты три потока:
- stdin -- стандартный входной поток. По умолчанию он назначен на клавиатуру;
- stdout -- стандартный выходной поток. По умолчанию он назначен на экран терминала;
- stderr -- выходной поток для печати информации об ошибках. Он также назначен по умолчанию на экран терминала.
Переменные stdin , stdout , stderr являются глобальными, они описаны в стандартном заголовочном файле "stdio.h. Операции файлового ввода-вывода могут использовать эти потоки, например, строка
вводит значение целочисленной переменной n из входного потока. Строка
выводит значение переменой n в выходной поток. Строка
По умолчанию, стандартный выходной поток и выходной поток для печати ошибок назначены на экран терминала. Однако операция перенаправления вывода в файл > действует только на стандартный выходной поток. Например, в результате выполнения командной строки
Функции scanf и printf ввода и вывода в стандартные потоки
Поскольку ввод из стандартного входного потока, по умолчанию назначенного на клавиатуру, и вывод в стандартный выходной поток, по умолчанию назначенный на экран терминала, используются особенно часто, библиотека функций ввода-вывода Си предоставляет для работы с этими потоками функции scanf и printf . Они отличаются от функций fscanf и fprintf только тем, что у них отсутствует первый аргумент, означающий поток ввода или вывода. Строка
Файл – это именованная область данных на каком-либо носителе информации.
Типы файлов (с точки зрения интерпретации информации в программе на Си):
Основные операции производимые над файлами:
- Открытие файлов.
- Чтение и запись данных.
- Закрытие файлов.
- Навигация по файлу.
- Обработка ошибок работы с файлами.
- Удаление и переименование файлов.
- Описание переменной
Для работы с файлами в программах на Си используется заголовочный файл stdio . h , в котором объявлен специальный тип данных — структура FILE , предназначенная для хранения атрибутов (параметров) файлов (указатель текущей позиции файла, признак конца файла, флаги индикации ошибок, сведения о буферизации и др.). Переменные типа FILE нельзя создавать вручную.
Для работы с файлом в программе нужно создать "указатель на файл".
Нужно осознавать, что каждое обращение к файлу выполняется через системный вызов. При этом э тот указатель не нужно разыменовывать при обращении к файлу, библиотечной функции передается сам этот указатель.
Чтение из текстового файла
Чтение и вывод в двоичные файлы
Запись строки
int fputs(const char *string, FILE *stream);
Функция возвращает число записанных символов, если все нормально, и EOF, если произошла ошибка.
Навигация по файлу
Режимы открытия файлов
r | только чтение |
w | Только запись. Если файл существовал, то он переписывается. |
a | Добавление: открытие файла для записи в конец, или создание файла. |
r+ | Открывает файл для обновления (чтение и запись). |
w+ | Открывает файл для обновления (чтение и запись), переписывая файл, если он существует. |
a+ | Открывает файл для записи в конец файла или для чтения. |
Во втором параметре дополнительно может указываться символ t и b для указания текстовый файл или двоичный (необязательно для многих операционных систем):
rt, wt, at, rt+, wt+, at+
rt, wt, at, rt+, wt+, at+
Помещение символа обратно в поток
int ungetc(int c, FILE *stream);
Функция возвращает код символа, если все успешно, и EOF, если произошла ошибка.
Чтение строки
char * fgets(char * buffer, int maxlen, FILE *stream);
В maxlen следует указать размер буфера, чтобы при записи в память функция чтения не вышла за его границы.
Функция считывает строку до символа перевода каретки '\n' или maxlen — что наступит раньше.
Функция возвращает указатель на buffer, если все нормально, и NULL, если возникла ошибка или достигнут конец файла.
Функция сброса ошибки
void clearerr(FILE *stream);
void perror(const char *string);
Форматированное чтение
int fscanf(FILE *stream, const char * format, [arg] . );
- больше 0 -- число успешно прочитанных переменных,
- 0 -- ни одна из переменных не была успешно прочитана,
- EOF -- ошибка или достигнут конец файла.
Буферизация
Функция очистки буфера:
int fflush(FILE *stream);
Функция возвращает 0, если все нормально, и EOF, если произошла ошибка.
Функция управления буфером:
void setbuf(FILE *stream, char *buffer);
Создает буфер размером BUFSIZ. Используется до ввода или вывода в поток.
Перемещение к началу файла
void rewind(FILE *stream);
Запись символа
int fputc(int c, FILE *stream);
Функция возвращает код записанного символа, если все нормально, и EOF, если произошла ошибка.
Запись в текстовый файл
Получение признака ошибки
int ferror(FILE *stream);
Функция возвращает ненулевое значение, если возникла ошибка.
Чтение текущего смещения в файле
long int ftell(FILE *stream);
Временные файлы
Функция создания временного файла:
Создает временный файл в режиме wb+. После закрытия файла, последний автоматически удаляется.
Функция генерации имени временного файла:
Операция чтения-записи всегда производится с текущей позиции в потоке. При открытии потока в режимах "r" и "w" указатель текущей позиции устанавливается на начальный байт потока. При открытии в режиме "a" указатель устанавливается на конец файла сразу за конечным байтом. И при выполнении операции чтения-записи указатель в потоке перемещается на новую позиции в соответствии с числом прочитанных или записанных байтов.
Однако вполне возможно, что нам потребуется считывать или записывать с какой-то определенной позиции в файле. Например, в айдиофайле в формате wav собственно звуковые данные расположены, начиная с 44 байта. И если, к примеру, мы хотим распознать звук из файла, что-то с ним сделать, то нам при считывании данных надо переместить указатель на соответствующую позицию.
В языке Си для управления позицией указателя в потоке применяется функция fseek() , которая имеет следующий прототип:
Второй параметр этой функции - смещение представляет числовое значение типа long , которое указывает, на какое количество байт надо переместить указатель. Это значение может быть отрицательным, если необходимо в потоке вернуться назад на некоторое количество байт.
Третий параметр - начало_отсчета задает начальную позицию, относительно которой идет смещение. В качестве этого параметра мы можем использовать одну из встроенных констант, определенных в файле stdio.h :
SEEK_SET : имеет значение 0 и представляет начало файла
SEEK_CUR : имеет значение 1 и представляет текущую позицию в потоке
SEEK_END : имеет значение 2 и представляет конец файла
Если перемещение указателя было успешно выполнено, то функция fseek() возвращает 0, иначе она возвращает ненулевое значение.
Кроме функции fseek() мы можем использовать для управления позицией указателя еще пару функций:
long ftell(FILE *) : получает текущую позицию указателя
void rewind(FILE *) : указатель устанавливается на начало потока
Применим функцию fseek в программе:
С помощью функции save в файл сохраняется массив структур. Затем в функции load считываем одну из структур по введенному номеру.
Для поиска нужной структуры вычисляем позицию:
Исходя из записи мы знаем, что первые 4 байта в файле отводятся под хранение количества структур, соответственно при чтении нам надо пропустить эти 4 байта.
Например, мы хотим получить первую структуру. По формуле получаем (1-1) * n + 4 = 4. То есть первая структура будет располагаться после 4-го байта. Аналогично вторая структура будет располагаться после n+4 байт, где n - это размер структуры.
Получив позицию, передаем ее в функцию fseek, перемещаемся в потоке и считываем после этого n байт.
При прямом доступе можно выполнять операции ввода/вывода, используя систему ввода/вывода языка С и функцию fseek() , которая устанавливает указатель текущей позиции в файле. Вот прототип этой функции:
Здесь уф — это указатель файла, возвращаемый в результате вызова функции fopen() , колич_байт — количество байтов, считая от начало_отсчета , оно определяет новое значение указателя текущей позиции, а начало отсчёта — это один из следующих макросов:
Поэтому, чтобы получить в файле доступ на расстоянии колич_байт байтов от начала файла, начало_отсчета должно равняться SEEK_SET . Чтобы при доступе расстояние отсчитывалось от текущей позиции, используйте макрос SEEK_CUR , а чтобы при доступе расстояние отсчитывалось от конца файла, нужно указывать макрос SEEK_END . При успешном завершении своей работы функция fseek() возвращает нуль, а в случае ошибки — ненулевое значение.
В следующей программе показано, как используется fseek() . Данная программа в определенном файле отыскивает некоторый байт, а затем отображает его. В командной строке нужно указать имя файла, а затем нужный байт, то есть его расстояние в байтах от начала файла.
Функцию fseek() можно использовать для доступа внутри многих значений одного типа, просто умножая размер данных на номер элемента, который вам нужен. Например, предположим, имеется список рассылки, который состоит из структур типа addr (определенных ранее). Чтобы получить доступ к десятому адресу в файле, в котором хранятся адреса, используйте следующий оператор:
Текущее значение указателя текущей позиции в файле можно определить с помощью функции ftell() . Вот ее прототип:
Функция возвращает текущее значение указателя текущей позиции в файле, связанном с указателем файла уф . При неудачном исходе она возвращает -1.
Обычно прямой доступ может потребоваться лишь для двоичных файлов. Причина тут простая — так как в текстовых файлах могут выполняться преобразования символов, то может и не быть прямого соответствия между тем, что находится в файле и тем байтом, к которому нужен доступ. Единственный случай, когда надо использовать fseek() для текстового файла — это доступ к той позиции, которая была уже найдена с помощью ftell() ; такой доступ выполняется с помощью макроса SEEK_SET , используемого в качестве начала отсчета.
Хорошо помните следующее: даже если в файле находится один только текст, все равно этот файл при необходимости можно открыть и в двоичном режиме. Никакие ограничения, связанные с тем, что файлы содержат текст, к операциям прямого доступа не относятся. Эти ограничения относятся только к файлам, открытым в текстовом режиме .
Файловая система языка С состоит из нескольких взаимосвязанных функций. Самые распространенные из них показаны в табл. 9.1. Для их работы требуется заголовок .
Заголовок предоставляет прототипы функций ввода/вывода и определяет следующие три типа: size_t , fpos_t и FILE . size_t и fpos_t представляют собой определенные разновидности такого типа, как целое без знака. А о третьем типе, FILE , рассказывается в следующем разделе.
Кроме того, в определяется несколько макросов. Из них к материалу этой главы относятся NULL , EOF , FOPEN_MAX , SEEK_SET , SEEK_CUR и SEEK_END . Макрос NULL определяет пустой (null) указатель. Макрос EOF , часто определяемый как -1, является значением, возвращаемым тогда, когда функция ввода пытается выполнить чтение после конца файла. FOPEN_MAX определяет целое значение, равное максимальному числу одновременно открытых файлов. Другие макросы используются вместе с fseek() — функцией, выполняющей операции прямого доступа к файлу.
Указатель файла
Указатель файла — это то, что соединяет в единое целое всю систему ввода/вывода языка С. Указатель файла — это указатель на структуру типа FILE . Он указывает на структуру, содержащую различные сведения о файле, например, его имя, статус и указатель текущей позиции в начало файла. В сущности, указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функций ввода/вывода. Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Чтобы объявить переменную-указатель файла, используйте такого рода оператор:
Открытие файла
Функция fopen() открывает поток и связывает с этим потоком определенный файл. Затем она возвращает указатель этого файла. Чаще всего (а также в оставшейся части этой главы) под файлом подразумевается дисковый файл. Прототип функции fopen() такой:
где имя_файла — это указатель на строку символов, представляющую собой допустимое имя файла, в которое также может входить спецификация пути к этому файлу. Строка, на которую указывает режим , определяет, каким образом файл будет открыт. В табл. 9.2 показано, какие значения строки режим являются допустимыми. Строки, подобные «r+b» могут быть представлены и в виде «rb+».
Как уже упоминалось, функция fopen() возвращает указатель файла. Никогда не следует изменять значение этого указателя в программе. Если при открытии файла происходит ошибка, то fopen() возвращает пустой (null) указатель.
В следующем коде функция fopen() используется для открытия файла по имени TEST для записи.
Хотя предыдущий код технически правильный, но его обычно пишут немного по-другому:
Этот метод помогает при открытии файла обнаружить любую ошибку, например, защиту от записи или полный диск, причем обнаружить еще до того, как программа попытается в этот файл что-либо записать. Вообще говоря, всегда нужно вначале получить подтверждение, что функция — fopen() выполнилась успешно, и лишь затем выполнять с файлом другие операции.
Хотя название большинства файловых режимов объясняет их смысл, однако не помешает сделать некоторые дополнения. Если попытаться открыть файл только для чтения, а он не существует, то работа fopen() завершится отказом. А если попытаться открыть файл в режиме дозаписи, а сам этот файл не существует, то он просто будет создан. Более того, если файл открыт в режиме дозаписи, то все новые данные, которые записываются в него, будут добавляться в конец файла. Содержимое, которое хранилось в нем до открытия (если только оно было), изменено не будет. Далее, если файл открывают для записи, но выясняется, что он не существует, то он будет создан. А если он существует, то содержимое, которое хранилось в нем до открытия, будет утеряно, причем будет создан новый файл. Разница между режимами r+ и w+ состоит в том, что если файл не существует, то в режиме открытия r+ он создан не будет, а в режиме w+ все произойдет наоборот: файл будет создан! Более того, если файл уже существует, то открытие его в режиме w+ приведет к утрате его содержимого, а в режиме r+ оно останется нетронутым.
Из табл. 9.2 видно, что файл можно открыть либо в одном из текстовых, либо в одном из двоичных режимов. В большинстве реализаций в текстовых режимах каждая комбинация кодов возврата каретки (ASCII 13) и конца строки (ASCII 10) преобразуется при вводе в символ новой строки. При выводе же происходит обратный процесс: символы новой строки преобразуются в комбинацию кодов возврата каретки (ASCII 13) и конца строки (ASCII 10). В двоичных режимах такие преобразования не выполняются.
Максимальное число одновременно открытых файлов определяется FOPEN_MAX. Это значение не меньше 8, но чему оно точно равняется — это должно быть написано в документации по компилятору.
Закрытие файла
Функция fclose() закрывает поток, который был открыт с помощью вызова fopen() .Функция fclose() записывает в файл все данные, которые еще оставались в дисковом буфере, и проводит, так сказать, официальное закрытие файла на уровне операционной системы. Отказ при закрытии потока влечет всевозможные неприятности, включая потерю данных, испорченные файлы и возможные периодические ошибки в программе. Функция fclose() также освобождает блок управления файлом, связанный с этим потоком, давая возможность использовать этот блок снова. Так как количество одновременно открытых файлов ограничено, то, возможно, придется закрывать один файл, прежде чем открывать другой. Прототип функции fclose() такой:
где уф — указатель файла, возвращенный в результате вызова fopen() . Возвращение нуля означает успешную операцию закрытия. В случае же ошибки возвращается EOF . Чтобы точно узнать, в чем причина этой ошибки, можно использовать стандартную функцию ferror() (о которой вскоре пойдет речь). Обычно отказ при выполнении fclose() происходит только тогда, когда диск был преждевременно удален (стерт) с дисковода или на диске не осталось свободного места.
Запись символа
В системе ввода/вывода языка С определяются две эквивалентные функции, предназначенные для вывода символов: putc() и fputc() . (На самом деле putc() обычно реализуется в виде макроса.) Две идентичные функции имеются просто потому, чтобы сохранять совместимость со старыми версиями С. В этой книге используется putc() , но применение fputc() также вполне возможно.
Функция putc() записывает символы в файл, который с помощью fopen() уже открыт в режиме записи. Прототип этой функции следующий:
где уф — это указатель файла, возвращенный функцией fopen() , a ch — выводимый символ. Указатель файла сообщает putc() , в какой именно файл следует записывать символ. Хотя ch и определяется как int , однако записывается только младший байт.
Если функция putc() выполнилась успешно, то возвращается записанный символ. В противном же случае возвращается EOF .
Чтение символа
Для ввода символа также имеются две эквивалентные функции: getc() и fgetc() . Обе определяются для сохранения совместимости со старыми версиями С. В этой книге используется getc() (которая обычно реализуется в виде макроса), но если хотите, применяйте fgetc() .
Функция getc() записывает символы в файл, который с помощью fopen() уже открыт в режиме для чтения. Прототип этой функции следующий:
где уф — это указатель файла, имеющий тип FILE и возвращенный функцией fopen() . Функция getc() возвращает целое значение, но символ находится в младшем байте. Если не произошла ошибка, то старший байт (байты) будет обнулен.
Если достигнут конец файла, то функция getc() возвращает EOF . Поэтому, чтобы прочитать символы до конца текстового файла, можно использовать следующий код;
Однако getc() возвращает EOF и в случае ошибки. Для определения того, что же на самом деле произошло, можно использовать ferror() .
Использование fopen(), getc(), putc(), и fclose()
Функции fopen() , getc() , putc() и fclose() — это минимальный набор функций для операций с файлами. Следующая программа, KTOD, представляет собой простой пример, в котором используются только функции putc() , fopen() и fclose() . В этой программе символы считываются с клавиатуры и записываются в дисковый файл до тех пор, пока пользователь не введет знак доллара. Имя файла определяется в командной строке. Например, если вызвать программу KTOD, введя в командной строке KTOD TEST , то строки текста будут вводиться в файл TEST.
Программа DTOS, являющаяся дополнением к программе KTOD, читает любой текстовый файл и выводит его содержимое на экран.
Испытывая эти две программы, вначале с помошью KTOD создайте текстовый файл, а затем с помошью DTOS прочитайте его содержимое.
Использование feof()
Как уже говорилось, если достигнут конец файла, то getc() возвращает EOF . Однако проверка значения, возвращенного getc() , возможно, не является наилучшим способом узнать, достигнут ли конец файла. Во-первых, файловая система языка С может работать как с текстовыми, так и с двоичными файлами. Когда файл открывается для двоичного ввода, то может быть прочитано целое значение, которое, как выяснится при проверке, равняется EOF . В таком случае программа ввода сообщит о том, что достигнут конец файла, чего на самом деле может и не быть. Во-вторых, функция getc() возвращает EOF и в случае отказа, а не только тогда, когда достигнут конец файла. Если использовать только возвращаемое значение getc() , то невозможно определить, что же на самом деле произошло. Для решения этой проблемы в С имеется функция feof() , которая определяет, достигнут ли конец файла. Прототип функции feof() такой:
Если достигнут конец файла, то feof() возвращает true (истина); в противном же случае эта функция возвращает нуль. Поэтому следующий код будет читать двоичный файл до тех пор, пока не будет достигнут конец файла:
Ясно, что этот метод можно применять как к двоичным, так и к текстовым файлам.
В следующей программе, которая копирует текстовые или двоичные файлы, имеется пример применения feof() . Файлы открываются в двоичном режиме, а затем feof() проверяет, не достигнут ли конец файла.
Ввод / вывод строк: fputs() и fgets()
Кроме getc() и putc() , в языке С также поддерживаются родственные им функции fgets() и fputs() . Первая из них читает строки символов из файла на диске, а вторая записывает строки такого же типа в файл, тоже находящийся на диске. Эти функции работают почти как putc() и getc() , но читают и записывают не один символ, а целую строку. Прототипы функций fgets() и fputs() следующие:
Функция fputs() пишет в определенный поток строку, на которую указывает cmp . В случае ошибки эта функция возвращает EOF .
Функция fgets() читает из определенного потока строку, и делает это до тех пор, пока не будет прочитан символ новой строки или количество прочитанных символов не станет равным длина -1. Если был прочитан разделитель строк, он записывается в строку, чем функция fgets() отличается от функции gets() . Полученная в результате строка будет оканчиваться символом конца строки (’0′). При успешном завершении работы функция возвращает cmp , а в случае ошибки — пустой указатель (null).
В следующей программе показано использование функции fputs() . Она читает строки с клавиатуры и записывает их в файл, который называется TEST. Чтобы завершить выполнение программы, введите пустую строку. Так как функция gets() не записывает разделитель строк, то его приходится специально вставлять перед каждой строкой, записываемой в файл; это делается для того, чтобы файл было легче читать:
Функция rewind()
Функция rewind() устанавливает указатель текущей позиции в файле на начало файла, указанного в качестве аргумента этой функции. Иными словами, функция rewind() выполняет «перемотку» (rewind) файла. Вот ее прототип:
где уф — это допустимый указатель файла.
Чтобы познакомиться с rewind() , изменим программу из предыдущего раздела таким образом, чтобы она отображала содержимое файла сразу после его создания. Чтобы выполнить отображение, программа после завершения ввода «перематывает» файл, а затем с помощью fback() читает его с самого начала. Обратите внимание, что сейчас файл необходимо открыть в режиме чтения/записи, используя в качестве аргумента, задающего режим, строку «w+».
Функция ferror()
Функция ferror() определяет, произошла ли ошибка во время выполнения операции с файлом. Прототип этой функции следующий:
где уф — допустимый указатель файла. Она возвращает значение true (истина), если при последней операции с файлом произошла ошибка; в противном же случае она возвращает false (ложь). Так как при любой операции с файлом устанавливается свое условие ошибки, то после каждой такой операции следует сразу вызывать ferror() , а иначе данные об ошибке могут быть потеряны.
В следующей программе показано применение ferror() . Программа удаляет табуляции из файла, заменяя их соответствующим количеством пробелов. Размер табуляции определяется макросом TAB_SIZE . Обратите внимание, что ferror() вызывается после каждой операции с файлом. При запуске этой программы указывайте в командной строке имена входного и выходного файлов.
Стирание файлов
Функция remove() стирает указанный файл. Вот ее прототип:
В случае успешного выполнения эта функция возвращает нуль, а в противном случае — ненулевое значение.
Следующая программа стирает файл, указанный в командной строке. Однако вначале она дает возможность передумать. Утилита, подобная этой, может пригодиться компьютерным пользователям-новичкам.
Дозапись потока
Для дозаписи содержимого выводного потока в файл применяется функция fflush() . Вот ее прототип:
Эта функция записывает все данные, находящиеся в буфере в файл, который указан с помощью уф . При вызове функции fflush() с пустым (null) указателем файла уф будет выполнена дозапись во все файлы, открытые для вывода.
После своего успешного выполнения fflush() возвращает нуль, в противном случае — EOF .
Установка текущей позиции в файле
int fsetpos(FILE *stream, const fpos_t *pos);
Функции возвращают 0, если все нормально, и не ноль, если произошла ошибка.
Изменение текущего смещения в файле
int fseek(FILE *stream, long int offset, int origin);
Значение origin может принимать три значения:
- SEEK_SET (0) – от начала файла.
- SEEK_CUR (1) – от текущей позиции.
- SEEK_END (2) – от конца файла.
Функция возвращает 0, если все нормально, и не ноль, если произошла ошибка.
Запись в двоичный файл
size_t fwrite(const void *buffer, size_t size, size_t num, FILE *stream);
Функция возвращает количество записанных блоков.
Если оно меньше num, то произошла ошибка.
Открытие файла
FILE *fopen(const char *filename, const char *mode);
где filename -- название файла, mode -- режим открытия.
Функция возвращает указатель на файл, если тот был успешно открыт. В противном случае -- NULL.
Название файла содержит только имя файла, если файл находится в текущем каталоге. Иначе необходимо указать абсолютный или относительный путь к файлу.
- "data.txt”
- "..\\files\\data.txt"
- "d:\\temp\\data.txt"
Чтение текущей позиции в файле
int fgetpos(FILE *stream, fpos_t *pos);
Чтение символа
int fgetc(FILE *stream);
Функция возвращает код символа, если все нормально, и EOF, если произошла ошибка или достигнут конец файла.
Чтение из двоичных файлов
size_t fread(void *buffer, size_t size, size_t num,FILE *stream);
Функция возвращает количество прочитанных блоков.
Если оно меньше num, то произошла ошибка или достигнут конец файла.
Закрытие файла
int fclose(FILE *stream);
где stream -- указатель на открытый файл.
- 0 – файл успешно закрыт.
- 1 – произошла ошибка закрытия файла.
Можно закрыть и открыть новый файл в одну команду:
FILE * freopen(const char *filename, const char *mode, FILE *stream);
Функция возвращает указатель на файл, если все нормально, и NULL, если возникла ошибка открытия нового файла или закрытия старого.
Проверка на достижение конца файла
int feof(FILE *stream);
где stream -- указатель на открытый файл
Функция возвращает 0, если файловый поток не кончился, и не ноль, если достигнут конец файла.
Форматированный вывод
int fprintf(FILE *stream, const char *format, [arg] . );
Функция возвращает число записанных символов, если все нормально, и отрицательное значение, если произошла ошибка.
Читайте также: