- Бинарные файлы
- Бинарные файлы
- fseek
- Примеры
- Работа с файлами в C
- Как работать с файлами
- Опции-параметры для открытия файлов
- Запись в текстовый файл
- Позиционирование указателя при записи файла
- Основные функции для работы с файлами в языке Си
- Открытие и закрытие файлового потока
- Чтение из файлов
- Запись в файл
- Считывание русских символов из файла
- Отличие работы с потоками в Си от других языков
- Заключение
- ГЛАВА 14. Файлы в языке Си
- Видео
Бинарные файлы
Бинарные файлы
Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.
Текстовые файлы позволяют хранить информацию в виде, понятном для человека. Можно, однако, хранить данные непосредственно в бинарном виде. Для этих целей используются бинарные файлы.
Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.
Запись в файл осуществляется с помощью функции
Функция возвращает число удачно записанных элементов. В качестве аргументов принимает указатель на массив, размер одного элемента, число элементов и указатель на файловый поток. Вместо массив, конечно, может быть передан любой объект.
Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread
Функция возвращает число удачно прочитанных элементов, которые помещаются по адресу ptr. Всего считывается count элементов по size байт. Давайте теперь считаем наше число обратно в переменную.
fseek
Одной из важных функций для работы с бинарными файлами является функция fseek
Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения
В случае удачной работы функция возвращает 0.
Дополним наш старый пример: запишем число, затем сдвинемся указатель на начало файла и прочитаем его.
Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.
В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция
используется для того, чтобы назначить переменной pos текущее положение. Функция
используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.
Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.
Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.
Примеры
1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.
4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.
5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).
6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.
Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.
Работа с файлами в C
В языке Си, файлы рассматриваются как место, куда можно сохранять информацию и читать данные, выводя их в другой файл, в консоль и другие места. Язык C предоставляет все необходимое, для выполнения операций с файлами. Запись или чтение из файла является потоком байтов. Прежде, чем осуществлять какую-либо манипуляцию с файлом, необходимо открыть поток.
Как работать с файлами
Для открытия потока передачи байтов следует писать следующую конструкцию:
FILE * переменная fopen(имя файла, режим открытия);
Если открытый файл уже не нужен, то его следует закрыть. В противном случае он будет потреблять лишнюю память, что может сказаться на производительности программы. Для закрытия файла применяется функция fclosе(переменная потока). Параметр функции указывает на переменную, в которую был сохранен открытый поток. Если поток был успешно закрыт, то функция возвращает 0, в противном случае EOF, указывающее на ошибку.
Пример открытия и закрытия потока:
int main(void)
<
FILE * f = fopen(«D:\text.txt», «w»); \\ Доступ к файлу, который находится по адресу D:\text.txt c параметром «W», который указывает на запись.
fclose(f); \\ Удаление потока
return 0;
>
Открытие потоков может сопровождаться ошибками. К примеру, при открытии файла для чтения, окажется, что такового не существует. Может быть недостаточно памяти для открытия файла и тому подобное. Если ошибка присутствует, то функция fopen() вернет NULL. Поэтому, чтобы такого не происходило, можно обработать ошибку с помощью функции perror.
Пример проверки на ошибку:
int main(void)
<
FILE * f; \\ Объявление переменной типа FILE
if((f= fopen(«D:\text25.txt», «r»))==NULL) \\ Проверка на наличие ошибки при открытии потока.
<
perror(«Error:»); Если есть ошибка, то будет выведен ее текст, в нашем случае это «Error No such file or directory».
exit(0); \\Принудительный выход из программы.
>
fclose(f); \\ Если файл открыт, то функция тут же его закроет.
return 0;
>
Для вывода ошибки в консоль, применяется функция perror(string), которая в качестве параметра принимает текст ошибки. В нашем примере не имеет смысла продолжать выполнение программы, поэтому ее принудительно останавливают с помощью функции exit(0).
Текст ошибки из параметра функции perror сопровождается и сообщением компилятора в виде No such file or directory, которое говорит о отсутствия такого файла в указанной директории. Однако некоторые параметры функции fopen позволяют создать несуществующий файл. Если файл с конкретным именем не находится, то он будет создан заново.
Опции-параметры для открытия файлов
Второй параметр функции fopen задает ограничения на действия открытого файла. От режима зависит как файл может быть обработан. Каждый режим представляет собой символ или их набор. Существуют следующие режимы открытия файловых потоков в языке Си:
Те же параметры применяются и для текстовых файлов, только без символа «b». Режимы задаются чтобы расширить возможности манипуляций с файлом и ограничить их до чтения или только записи. Также режимы задают формат файла — бинарный или текстовый. Неправильно установленный режим может привести либо к ошибке, либо к некорректной интерпретации файла.
Запись в текстовый файл
Для записи символов или строки в текстовый файл используется функция fputs(). Синтаксис данной функции следующий: int fputs( const char ch, FILE f); Здесь первый параметр ch принимает записываемую строку или символ. Второй параметр указывает на открытый файловый поток. Если при записи произошла ошибка, то функция возвращает значение ошибки EOF.
Пример записи теста в файл:
int main(void)
<
char * mes = «Hello, my name is Vova»; \\Сохраняем строку для записи в файл
char * fName = «D://text5.txt»; \\Сохраняем путь к файлу
char c[256];
FILE * f;
if((f= fopen(fName, «w»))==NULL) \\Если возникла ошибка при открытии файла, то вывести сообщение «Error» и выйти из программы
<
perror(«Error»);
return 1;
>
fputs(mes, f); \\ Запись в файл строки в переменной mes
Так как в данном примере был использован режим открытия «W», то нам открывается пустой файл для записи. Если после выполнения данного кода открыть файл text5.txt, то в его содержании будет строка «Hello, my name is Vova».
Позиционирование указателя при записи файла
Режим «r» или «w» устанавливает каретку записи в начало файла. Режим «a» устанавливает каретку в конец имеющихся данных. Таким образом первый вариант перезаписывает файл, второй дописывает. При записи или чтении каретка передвигается на позицию, равную числу прочитанных или записанных байтов, или символов.
Однако часто необходимо выполнять поток байтов с конкретной позиции в файле, к примеру, со средины или с 53-й позиции. Для этого необходимо воспользоваться функцией fseek(), которая перемещает каретку в указанную позицию.
У данной функции следующий синтаксис: fseek(файл, количество байтов для смещения, начальная позиции);
Пояснение к функции:
Пример использования функции fseek():
int main(void) <
int i; \\ Переменная для сохранения символов в цикле
char * str = «Hello»; \\ Первая строка записываемая в файл
char * final_str; \\ Строка для дописывания в файл
File * f = fopen(«text.txt», w);
fputs(str, f); \\ Записываем первую строку в файл
fseek(f, 6, SEEK_SET); \\ Устанавливаем каретку на 6-ю позицию
fputs(«World!», f); \\ С шестой позиции записываем в файл строку «World!»
fseek(f, 0, SEEK_SET); \\ Устанавливаем позицию каретки в начало
while ((i = getc(f))!=EOF) \\ Получаем каждый символ файла и сохраняем его в переменную «final_str»
<
*final_str +=(char *)i;
>
printf(«Содержание файла:», final_str); \\ Выводим содержимое файла на консоль
fclose(f);
>
Основные функции для работы с файлами в языке Си
Есть ряд функций в языке Си, который позволяет манипулировать с файлами и потоками передачи данных. Основные из них были рассмотрены выше. Однако необходимо ознакомиться с их полным списком.
Открытие и закрытие файлового потока
Чтение из файлов
Запись в файл
Считывание русских символов из файла
setlocale(LC_ALL, «Russian») — вызов данной функции позволяет настроить поток на работу с кириллицей.
Отличие работы с потоками в Си от других языков
Как видно, язык Си не использует обращение к объектам для оперирования с файлами. Для открытия потока здесь даже не применяется конструкция new IOstream(). Ведь Cи является процедурным языком и в нем не используется парадигма ООП. Тем не менее, если открывать поток без сохранения в переменную, то дальнейшее применение функций должно сопровождаться записью в их параметры директории к файлу, какая была указана при открытии.
Ранее мы создавали переменную “f” для сохранения потока. Без нее всегда придется писать строку на подобие “C\:files\index.txt”. Данный подход достаточно неудобный и требует постоянной записи строки. При этом, все равно придется закрывать поток. Проще сохранить его в переменную – это дает гарантию тог, что работа всегда осуществляется с данным потоком.
Заключение
Работа с файлами является мощным инструментом каждого языка программирования. Ведь можно не только изменять содержание текстовых файлов, но и аудио, видео и и даже изображений через запись и чтение байтов. Так как в файлы записываются строки и символы, то перед записью их можно обрабатывать различными функциями или с помощью событий.
Большинство сложных приложений — аудиоконверторы, проигрыватели, графические редакторы и другие, работают с файлами. Важно уметь использовать возможности Си для работы с файловыми потоками, чтобы писать ложные автоматизированные сервисы.
Работу с файлами изучить достаточно просто. Ведь Си — это не объектно-ориентированный язык и обладает достаточно малым набором простых функций для обработки файлов. Режимы открытия файлов также не сложно запомнить. Ведь они практически идентичны. А благодаря наличию в языке встроенных функций по предоставлению информации об ошибках, можно гарантированно узнать, что файл так и не был создан, или в него ничего не было записано.
ГЛАВА 14. Файлы в языке Си
Файл – это набор данных, размещенный на внешнем носителе и рассматриваемый в процессе обработки как единое целое. В файлах размещаются данные, предназначенные для длительного хранения.
Различают два вида файлов: текстовые и бинарные.
Текстовые файлы представляют собой последовательность ASCII символов и могут быть просмотрены и отредактированы с помощью любого текстового редактора. Эта последовательность символов разбивается на строки символов, при этом каждая строка заканчивается двумя кодами «перевод строки», «возврат каретки»: 13 и 10 (0xD и 0xA).
Бинарные (двоичные) файлы представляют собой последовательность данных, структура которых определяется программно.
В языке Си не предусмотрены никакие заранее определенные структуры файлов. Все файлы рассматриваются компилятором как последовательность (поток байт) информации.
Для файлов определен маркер или указатель чтения-записи данных, который определяет текущую позицию доступа к файлу. Напомним, что с началом работы любой программы автоматически открываются стандартные потоки stdin и stdout.
В языке Си имеется большой набор функций для работы с файлами, большинство которых находятся в библиотеках stdio.h и io.h. При этом потоки данных, с которыми работают функции ввода-вывода данных по умолчанию, буферизированы. Это означает, что при открытии потока с ним автоматически связывается определенный участок ОП, который и называется буфером. Все операции чтения-записи ведутся через этот буфер. Его размер фиксирован специальной константой BUFSIZ, которая определена в файле stdio.h как 512 (хотя программно ее можно изменять).
Открытие файла
Каждому файлу в программе присваивается внутреннее логическое имя, используемое в дальнейшем при обращении к нему. Логическое имя (идентификатор файла) – это указатель на файл, т.е. на область памяти, где содержится вся необходимая информация о файле.
Формат объявления указателя на файл следующий:
FILE *ID_указателя_на_файл;
FILE – идентификатор структурного типа, описанный в стандартной библиотеке stdio.h и содержащий следующую информацию:
struct FILE <
short level; | – число оставшихся в буфере непрочитанных байт; обычный размер буфера – 512 байт; как только level = 0, в буфер из файла читается следующий блок данных; |
unsigned flags; | – флаг статуса файла – чтение, запись, дополнение; |
char fd; | – дескриптор файла, т.е. число, определяющее его номер; |
unsigned char hold; | – непереданный символ, т.е. ungetc-символ; |
short bsize; | – размер внутреннего промежуточного буфера; |
unsigned char buffer; | – значение указателя для доступа внутри буфера; задает начало буфера, начало строки или текущее значение указателя внутри буфера в зависимости от режима буферизации; |
unsigned char *curp; | – текущее значение указателя для доступа внутри буфера; задает текущую позицию в буфере для обмена с программой; |
unsigned istemp; | – флаг временного файла; |
short token; | – флаг при работе с файлом; |
Прежде чем начать работать с файлом, т.е. получить возможность чтения или записи информации в файл, его нужно открыть для доступа.
Для этого обычно используется функция
FILE* fopen(char * ID_файла, char *режим);
Данная функция берет внешнее представление – физическое имя файла на носителе (дискета, винчестер) и ставит ему в соответствие логическое имя (программное имя – указатель файла).
Физическое имя, т.е. ID файла и путь к нему задается первым параметром – строкой, например, “a:Mas_dat.dat” – файл с именем Mas_dat и расширением dat, находящийся на дискете, “d:\\work\\Sved.txt” – файл с именем Sved и расширением txt, находящийся на винчестере в каталоге work.
Внимание. Обратный слеш «\», как специальный символ в строке записывается дважды.
При успешном открытии функция fopen возвращает указатель на файл (в дальнейшем – указатель файла). При ошибке возвращается NULL. Данная ситуация обычно возникает, когда неверно указывается путь к открываемому файлу, например, если указать путь, запрещенный для записи.
Второй параметр – строка, в которой задается режим доступа к файлу.
Возможные значения данного параметра следующие:
w – файл открывается для записи (write); если файла с заданным именем нет, то он будет создан; если же такой файл уже существует, то перед открытием прежняя информация уничтожается;
r – файл открывается для чтения (read); если такого файла нет, то возникает ошибка;
a – файл открывается для добавления (append) новой информации в конец;
r+ (w+) – файл открывается для редактирования данных, т.е. возможны и запись, и чтение информации;
a+ – то же, что и для a, только запись можно выполнять в любое место файла (доступно и чтение файла);
t – файл открывается в текстовом режиме;
b – файл открывается в двоичном режиме;
Последние два режима используются совместно с рассмотренными выше. Возможны следующие комбинации режимов доступа: “w+b”, “wb+”, “rw+”, “w+t”, “rt+”, а также некоторые другие комбинации.
По умолчанию файл открывается в текстовом режиме.
Текстовый режим отличается от двоичного тем, что при открытии файла как текстового пара символов «перевод строки» и «возврат каретки» заменяется на один символ «перевод строки» для всех функций записи данных в файл, а для всех функций вывода – наоборот – символ «перевод строки» заменяется на два символа – «перевод строки» и «возврат каретки».
Пример открытия файла:
FILE *f; – объявляется указатель на файл f;
f = fopen (» d:\\work\\Dat_sp.dat «, «w»); – открывается для записи файл с логическим именем f, имеющий физическое имя Dat_sp.dat и находящийся на диске d в каталоге work, или более кратко:
FILE *f = fopen («d:\\work\\Dat_sp.dat», «w»);
Закрытие файла
После работы с файлом доступ к нему необходимо закрыть с помощью функции
int fclose (указатель файла);
Например, для предыдущего примера файл закрывается так: fclose (f);
Для закрытия нескольких файлов введена функция:
void fcloseall (void);
Если требуется изменить режим доступа к открытому в настоящий момент файлу, то его необходимо сначала закрыть, а затем вновь открыть с другими правами доступа. Для этого используется функция
FILE* freopen (char *ID_файла, char *режим, FILE *указатель_файла);
которая сначала закрывает файл, заданный в третьем параметре (указатель файла), как это выполняет функция fclose, а затем выполняет действия, аналогичные функции fopen, используя указанные первый и второй параметры (открывает файл с ID_файла и правами доступа режим).
В языке Си имеется возможность работы с временными файлами, которые нужны только в процессе работы программы и должны быть удалены после выполнения некоторых вычислений. В этом случае используется функция
FILE* tmpfile (void);
которая создает на диске временный файл с правами доступа w+b. После завершения работы программы или закрытия этого (временного) файла он автоматически удаляется.
Запись-чтение информации
Все действия по чтению-записи данных в файл можно разделить на три группы:
– операции посимвольного ввода-вывода;
– операции построчного ввода-вывода;
– операции ввода-вывода по блокам.
Рассмотрим основные функции для записи-чтения данных из файлов.
Для работы с текстовыми файлами в библиотеке языка Си содержится достаточно много функций, самыми распространенными из которых являются функции
fprintf, fscanf, fgets, fputs.
Формат параметров этих функций практически такой же, как и формат рассмотренных ранее (см. разд. 5.3, 5.4) функций printf, scanf, gets и puts. Так же практически совпадают и действия этих функций. Отличие состоит в том, что printf и другие функции работают по умолчанию с экраном монитора и клавиатурой, а функции fprintf и другие – с файлом, указатель которого является одним из параметров этих функций.
Рассмотрим общий пример создания текстового файла:
puts(“Open File Error!”);
fprintf(f1,”\t Файл результатов \n”);
fprintf(f1,” %d плюс %d = %d\n”, a, b, a+b);
Просмотрев содержимое файла любым текстовым редактором, можно убедиться, что данные в нем располагаются точно так, как на экране, если воспользоваться функцией printf с такими же списками параметров.
Создание текстовых результирующих файлов обычно необходимо для оформления отчетов, различных документов, а также других текстовых материалов.
Бинарные (двоичные) файлы обычно используются для организации баз данных, состоящих, как правило, из объектов структурного типа. При чтении-записи бинарных файлов удобнее всего пользоваться функциями fread и fwrite, которые выполняют ввод-вывод данных блоками.
Такой способ обмена данными требует меньше времени.
unsigned fread (void *p, unsigned size, unsigned n, FILE *f);
выполняет считывание из файла f n блоков размером size байт каждый в область памяти, адрес которой p. В случае успеха функция возвращает количество считанных блоков. При возникновении ошибки или по достижении признака окончания файла – значение EOF (End Of File – признак окончания файла).
Обратное действие выполняет функция:
unsigned fwrite (void *p, unsigned size, unsigned n, FILE *f);
при вызове которой в файл f будет записано n блоков размером size байт каждый из области памяти, начиная с адреса p.
Позиционирование в файле
Каждый открытый файл, как уже отмечалось, имеет скрытый указатель на текущую позицию в нем. При открытии файла этот указатель устанавливается на начало данных, и все операции в файле будут производиться с данными, начинающимися в этой позиции.
При каждом выполнении функции чтения или записи указатель смещается на количество прочитанных или записанных байт, т.е. устанавливается после прочитанного или записанного блока данных в файле – это последовательный доступ к данным.
В языке Си/С++ можно установить указатель на некоторую заданную позицию в файле. Для этого используют стандартную функцию fseek, которая позволяет выполнить чтение или запись данных в произвольном порядке.
Декларация функции позиционирования следующая:
int fseek(FILE *f, long size, int code);
Значение параметра size задает количество байт, на которое необходимо сместить указатель в файле f, в направлении параметра code, который может принимать следующие значения:
– смещение от начала файла – | (SEEK_SET); |
– смещение от текущей позиции – | (SEEK_CUR); |
– смещение от конца файла – | (SEEK_END). |
Таким образом, смещение может быть как положительным, так и отрицательным, но нельзя выходить за пределы файла.
В случае успеха функция возвращает нулевое значение, а в случае ошибки (например, попытка выхода за пределы файла) – единицу.
Доступ к файлу с использованием функции позиционирования (fseek) называют произвольным доступом.
Иногда нужно определить текущее положение в файле. Для этого используют функцию со следующей декларацией:
long ftell(FILE *f);
которая возвращает значение указателя на текущую позицию в файле или –1 в случае ошибки.
Видео
как работать с бинарными файлами в сиСкачать
Язык Си с нуля - Урок 37 - Работа с файлом в бинарном режимеСкачать
Язык Си с нуля - Урок 40 - Работа с файлом. Разница текстового и бинарного режимаСкачать
Бинарные файлы C++. В чем прикол? Как с ними работать?Скачать
Язык Си с нуля - Урок 38 - ДЗ уроков 35, 36, 37 (работа с файлом в текстовом и бинарном режиме)Скачать
Программирование на С++. Урок 64. Бинарный доступ к файлу.Скачать