C библиотека для работы с файлами

C библиотека для работы с файлами

БлогNot. Лекции по C/C++: работа с файлами (stdio.h)

Лекции по C/C++: работа с файлами (stdio.h)

В лекции рассмотрен классический способ работы с файлами в C/C++, основанный на библиотеке stdio.h и доступе к данным через структуру FILE . Альтернативный современный механизм работы с файлами в языке C++ на основе потоков и библиотек , , будет изучен в следующей лекции.

Базовые функции для работы с файлами описаны в библиотеке stdio.h . Вся работа с файлом выполняется через файловую переменную — указатель на структуру типа FILE , определённую в стандартной библиотеке:

Открыть файл можно функцией fopen , имеющей 2 параметра:

Параметр имя_файла может содержать относительный или абсолютный путь к открываемому файлу:

1) "data.txt" — открывается файл data.txt из текущей папки

Важно: при запуске exe-файла "текущая папка" – та, где он находится; при отладке в IDE папка может быть иной, например, в Visual Studio при открытом консольном решении с именем Console файл следует разместить в папке Console/Console , а при запуске исполняемого файла не из IDE – в папке Console/Debug .

2) "f:\my.dat" — открывается файл my.dat из головной папки диска f:

3) имя файла запрашивается у пользователя:

Параметр режим_доступа определяет, какие действия будут разрешены с открываемым файлом, примеры его возможных значений:

1) "rt" — открываем для чтения текстовый файл;

2) "r+b" — открываем для произвольного доступа (чтение и запись) бинарный файл;

3) "at" – открываем текстовый файл для добавления данных в конец файла;

4) "w" — открываем файл для записи без указания того, текстовый он или бинарный.

Фактически, указание "r" или "t" не накладывает каких-либо ограничений на методы, которые мы будем применять для чтения или записи данных.

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

1) стандартный обработчик ferror (см. пособиe, п.8.7);

2) сравнить указатель, который вернула fopen , с константой NULL ( nullptr ) из стандартной библиотеки:

Пример. Приложение проверяет, удалось ли открыть файл из текущей папки, имя файла запрашивается у пользователя (Visual Studio)

Важно! Функции, возвращающие указатель, в том числе, fopen , считаются небезопасными в ряде новых компиляторов, например, Visual Studio 2015. Если их использование приводит не просто к предупреждению, а к генерации ошибок, есть 2 основных способа решения проблемы:

1) в соответствии с рекомендациями компилятора, заменить старые названия функций на их безопасные версии, например, strcpy на strcpy_s и fopen на fopen_s . При этом может измениться и способ вызова функций, например,

2) в начало файла (до всех #include ) включить директиву

Если используется предкомпиляция, то можно определить этот макрос в заголовочном файле stdafx.h .

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

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

  • fscanf — для чтения
  • fprintf — для записи

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

Пример. Файл text.txt в текущей папке приложения имеет следующий вид:

Прочитаем его как последовательность вещественных чисел.

1. Функции семейства scanf возвращают целое число — количество значений, которые успешно прочитаны в соответствии с указанным форматом. В реальных приложениях эту величину следует проверять в коде:

2. На "восприятие" программой данных может влиять установленная в приложении локаль. Например, если до показанного кода выполнен оператор результат работы кода может измениться (для русской локали разделителем целой и дробной части числа является запятая, а не точка).

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

4. Код показывает, как читать из файла заранее неизвестное количество значений – это позволяет сделать стандартная функция feof (проверка, достигнут ли конец файла; вернёт не 0, если прочитано всё).

5. Распространённый в примерах из Сети код вида

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

В качестве примера форматной записи в файл сохраним массив a из 10 целочисленных значений в файле с именем result.txt по 5 элементов в строке:

Важно! Ввод/вывод функциями библиотеки stdio.h буферизован, то есть, данные "пропускаются" через область памяти заданного размера, обмен данными происходит не отдельными байтами, а "порциями". Поэтому перед чтением данных желательно очищать буфер от возможных "остатков" предыдущего чтения методом fflush , а после записи данных следует обязательно закрывать файл методом fclose , иначе данные могут быть потеряны. Заметим, что консольный ввод/вывод "обычными" методами scanf и printf также буферизован.

Теперь рассмотрим текстовый файл, состоящий из неструктурированных строк (абзацев) текста, разделённых символами перевода строки. При работе с такими данными могут потребоваться следующие функции:

  • fgetc и fputc — для посимвольного чтения и посимвольной записи данных;
  • fgets и fputs — для чтения и записи строк с указанным максимальным размером.

Как и в случае с функциями для чтения форматированных данных, у всех этих методов имеются аналоги для работы со стандартным вводом/выводом.

Пример. Читая файл, определить длину каждой строки в символах. Для решения задачи воспользуемся тем фактом, что строки завершаются символом "перевод строки" ( ‘
‘ ). Предполагается, что файл уже открыт для чтения.

Читайте также:  Asus eah5770 series драйвер

Важно! Из-за особенностей реализации fgetc , без последней проверки за телом цикла код мог "не обратить внимания", например, на последнюю строку файла, состоящую только из пробелов и не завершающуюся переводом строки.

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

Важно! Без дополнительной обработки прочитанные из файла строки при выводе будут содержать лишние пустые строки между строками данных. Это происходит потому, что функция fgets читает строку файла вместе с символом перевода строки (точней, под Windows — с парой символов
, интерпретируемых как один), а puts добавляет к выводимой строке ещё один перевод строки.

Если максимальная длина строки принципиально не ограничена, помочь может либо предварительное посимвольное чтение файла для её определения, либо работа с файлом как с бинарными данными. Бинарный файл отличается от текстового тем, что необязательно состоит из печатаемых символов со стандартными разделителями между ними. Соответственно, для него не имеет смысла понятие "строки данных", а основной способ работы с ним – чтение и запись наборов байт указанного размера. Основные функции для чтения и записи бинарных данных – fread и fwrite соответственно. В базовой реализации они имеют по 4 параметра:

  • void *buffer — нетипизированный указатель на место хранения данных;
  • size_t (unsigned) size — размер элемента данных в байтах.
  • size_t count — максимальное количество элементов, которые требуется прочитать (записать);
  • FILE *stream — указатель на структуру FILE

Пример. Целочисленный массив a запишем в двоичный файл.

Учитывая, что данные массива хранятся в последовательно идущих адресах памяти, цикл for для записи мы могли заменить одним оператором:

Подход к чтению данных с помощью fread аналогичен. Например, если файл уже открыт для чтения в режиме "rb":

Для файлов, открытых в режиме "r+b", разрешены и чтение, и запись (произвольный доступ). Поэтому при работе с такими файлами нужны функции позиционирования файлового указателя:

  • функции fgetpos и ftell позволяют выполнить чтение текущей позиции указателя в файле;
  • функции fseek и fsetpos позволяют осуществить переход к нужной позиции в файле.

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

Материал для чтения из пособия: пп. 8.6-8.11. Обратите внимание на таблицы с описанными прототипами функций ввода/вывода.

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

Про conio.h и почему его не надо использовать:

Для ввода/вывода через цветную консоль во многих источниках используются методы библиотеки conio.h . Следует учитывать, что её реализации в компиляторах от Borland и Microsoft значительно отличаются, а в компиляторах под Unix/Linux реализации conio.h могут отсутствовать.

Как вариант, в компиляторах Visual Studio можно использовать аналоги conio.h от сторонних разработчиков, например, открытый проект coniow.h. Законченный пример кода, реализующего несложное консольное меню для Visual Studio, есть вот здесь. Предполагается, что к проекту подключены заголовочный файл coniow.h и файл исходного кода coniow.c .

Оглавление серии

05.11.2015, 09:20; рейтинг: 29693

Для удобства обращения информация в запоминающих устройствах хранится в виде файлов.

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

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

Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются FAT (FAT – File Allocation Table, таблица размещения файлов), NTFS, UDF (используется на компакт-дисках).

Существуют три основные версии FAT: FAT12, FAT16 и FAT32. Они отличаются разрядностью записей в дисковой структуре, т.е. количеством бит, отведённых для хранения номера кластера. FAT12 применяется в основном для дискет (до 4 кбайт), FAT16 – для дисков малого объёма, FAT32 – для FLASH-накопителей большой емкости (до 32 Гбайт).

Рассмотрим структуру файловой системы на примере FAT32.

Файловая структура FAT32

Устройства внешней памяти в системе FAT32 имеют не байтовую, а блочную адресацию. Запись информации в устройство внешней памяти осуществляется блоками или секторами.

Сектор – минимальная адресуемая единица хранения информации на внешних запоминающих устройствах. Как правило, размер сектора фиксирован и составляет 512 байт. Для увеличения адресного пространства устройств внешней памяти сектора объединяют в группы, называемые кластерами.

Кластер – объединение нескольких секторов, которое может рассматриваться как самостоятельная единица, обладающая определёнными свойствами. Основным свойством кластера является его размер, измеряемый в количестве секторов или количестве байт.

Файловая система FAT32 имеет следующую структуру.

Нумерация кластеров, используемых для записи файлов, ведется с 2. Как правило, кластер №2 используется корневым каталогом, а начиная с кластера №3 хранится массив данных. Сектора, используемые для хранения информации, представленной выше корневого каталога, в кластеры не объединяются.
Минимальный размер файла, занимаемый на диске, соответствует 1 кластеру.

Загрузочный сектор начинается следующей информацией:

  • EB 58 90 – безусловный переход и сигнатура;
  • 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
  • 00 02 – количество байт в секторе (обычно 512);
  • 1 байт – количество секторов в кластере;
  • 2 байта – количество резервных секторов.
Читайте также:  H2o технологии поверка счетчиков отзывы

Кроме того, загрузочный сектор содержит следующую важную информацию:

  • 0x10 (1 байт) – количество таблиц FAT (обычно 2);
  • 0x20 (4 байта) – количество секторов на диске;
  • 0x2С (4 байта) – номер кластера корневого каталога;
  • 0x47 (11 байт) – метка тома;
  • 0x1FE (2 байта) – сигнатура загрузочного сектора ( 55 AA ).

Сектор информации файловой системы содержит:

  • 0x00 (4 байта) – сигнатура ( 52 52 61 41 );
  • 0x1E4 (4 байта) – сигнатура ( 72 72 41 61 );
  • 0x1E8 (4 байта) – количество свободных кластеров, -1 если не известно;
  • 0x1EС (4 байта) – номер последнего записанного кластера;
  • 0x1FE (2 байта) – сигнатура ( 55 AA ).

Таблица FAT содержит информацию о состоянии каждого кластера на диске. Младшие 2 байт таблицы FAT хранят F8 FF FF 0F FF FF FF FF (что соответствует состоянию кластеров 0 и 1, физически отсутствующих). Далее состояние каждого кластера содержит номер кластера, в котором продолжается текущий файл или следующую информацию:

  • 00 00 00 00 – кластер свободен;
  • FF FF FF 0F – конец текущего файла.

Корневой каталог содержит набор 32-битных записей информации о каждом файле, содержащих следующую информацию:

  • 8 байт – имя файла;
  • 3 байта – расширение файла;

Корневой каталог содержит набор 32-битных записей информации о каждом файле, содержащих следующую информацию:

  • 8 байт – имя файла;
  • 3 байта – расширение файла;
  • 1 байт – атрибут файла:
  • 1 байт – зарезервирован;
  • 1 байт – время создания (миллисекунды) (число от 0 до 199);
  • 2 байта – время создания (с точностью до 2с):
  • 2 байта – дата создания:
  • 2 байта – дата последнего доступа;
  • 2 байта – старшие 2 байта начального кластера;
  • 2 байта – время последней модификации;
  • 2 байта – дата последней модификации;
  • 2 байта – младшие 2 байта начального кластера;
  • 4 байта – размер файла (в байтах).

В случае работы с длинными именами файлов (включая русские имена) кодировка имени файла производится в системе кодировки UTF-16. При этого для кодирования каждого символа отводится 2 байта. При этом имя файла записывается в виде следующей структуры:

  • 1 байт последовательности;
  • 10 байт содержат младшие 5 символов имени файла;
  • 1 байт атрибут;
  • 1 байт резервный;
  • 1 байт – контрольная сумма имени DOS;
  • 12 байт содержат младшие 3 символа имени файла;
  • 2 байта – номер первого кластера;
  • остальные символы длинного имени.

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

Работа с файлами в языке Си

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

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

Открытие файла осуществляется с помощью функции fopen() , которая возвращает указатель на структуру типа FILE , который можно использовать для последующих операций с файлом.

  • "r" — открыть файл для чтения (файл должен существовать);
  • "w" — открыть пустой файл для записи; если файл существует, то его содержимое теряется;
  • "a" — открыть файл для записи в конец (для добавления); файл создается, если он не существует;
  • "r+" — открыть файл для чтения и записи (файл должен существовать);
  • "w+" — открыть пустой файл для чтения и записи; если файл существует, то его содержимое теряется;
  • "a+" — открыть файл для чтения и дополнения, если файл не существует, то он создаётся.

Возвращаемое значение — указатель на открытый поток. Если обнаружена ошибка, то возвращается значение NULL .

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

Возвращаемое значение: значение 0, если поток успешно закрыт; константа EOF , если произошла ошибка.

Чтение символа из файла:

Аргументом функции является указатель на поток типа FILE . Функция возвращает код считанного символа. Если достигнут конец файла или возникла ошибка, возвращается константа EOF .

Запись символа в файл:

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

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

Функции fgets() и fputs() предназначены для ввода-вывода строк, они являются аналогами функций gets() и puts() для работы с файлами.

Символы читаются из потока до тех пор, пока не будет прочитан символ новой строки ‘
’ , который включается в строку, или пока не наступит конец потока EOF или не будет прочитано максимальное символов. Результат помещается в указатель на строку и заканчивается нуль- символом ‘’ . Функция возвращает адрес строки.

Результат выполнения — 2 файла

Работа с файлами в C++ описана здесь.

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

Наиболее частые операции следующее:

  1. Операторы перенаправления вводавывода – >
  1. Методы записи и чтения строк getline() и get() c put()
  1. Потоковая запись и чтение методами write() и read()
  1. Методы открытиясоздания и закрытия файлов open() и close()
  1. Методы проверки открыт ли файл is_open() и достигнут ли конец файла eof()
  1. Настройка форматированного вывода для >> с помощью width() и precision()
  1. Операции позиционирования tellg(), tellp() и seekg(), seekp()
Читайте также:  Canon digital ixus 850 is

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

Класс ifstream

Предоставляет возможности для чтения файлов. Открыть файл можно двумя способами: вызвав метод open() или указав путь к нему в конструкторе. Вам необходимо подготовить текстовый файл, перед тем, как начать набирать код. На диске d создайте папку с именем 1 и в ней создайте файл с расширением txt – “файл.txt”.

Открытие файла в конструкторе выглядит так:

Так мы просим открыть файл txt с именем файл.txt, который лежит в папке с названием 1, а папка находится на диске d.

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

Открыв файл, желательно прописать проверку: открылся ли он? Так как есть ряд причин, по которым файл может не открыться, а мы этого не увидим. Например, файла с указанным именем нет в прописанной папке или путь указан неверно. Можно пойти двумя путями: проверить переменную файла в логическом выражении (применив оператор “!”, к примеру) или использовать метод is_open() :

Так все отработает нормально и файл откроется:

Теперь попробуйте вписать название папки не 1, а 2 ifstream file ( "d:\ color : #ff0000;">2 \файл.txt”); и снова запустите программу. Так как папки с указанным именем мы не создавали, то и файл, естественно, не может быть открыт:

Второй вариант проверки с использованием метода is_open() :

Метод is_open() вернет 1, если файл найден и успешно открыт. Иначе вернет 0 и сработает код прописанный в блоке else .

Если файл не открыт – желательно обработать ошибку. Как правило, если вся работа программы связана с файлом пишут некое сообщение в консоль, и ставят выход из программы. При серьезных ошибках принято возвращать некий код выполнения (число), который будет характеризовать ту или иную ошибку. Коды для каждого вида ошибок автор программы может придумывать свои. Один из способов обработки ошибок в программе мы рассматривали в статье Исключения в С++.

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

Оператор считывания >>

Так же как и в iostream считывание можно организовать оператором >> , который указывает в какую переменную будет произведено считывание:

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

Кстати этот оператор достаточно удобен, если стоит задача разделить файл на слова:

Методы getline() и get()

Считывание целой строки до перевода каретки производится так же как и в iostream методом getline(). Причем рекомендуется использовать его переопределеную версию в виде функции, если считывается строка типа string:

Если же читать нужно в массив символов char[], то либо get() либо getline() именно как методы:

Принцип в общем тот же, что и в аналогах из iostream: Указывается в параметрах буфер (переменная, куда будет производиться чтение), или точнее указатель на блок памяти (если переменная объявлена статически: char buffer[255] к примеру, то пишется в параметры &buffer), указывается максимальное количество считываемого (в примере это n), дабы не произошло переполнение и выход за пределы буфера и по необходимости символ-разделитель, до которого будет считка (в примере это пробел). Надеюсь я не больно наступлю на хобот фанатикам Си, если сажу что эти две функции на 99% взаимозаменяемы, и на 95% могут быть заменены методом read() .

Метод read()

Похож на предыдущий пример?

Собственно тут тот же результат – считается указанное количество символов. Исключение только в том, что нельзя указать разделитель. read() применяется для неформатированного ввода. Призван в первую очередь читать бинарные файлы. Поскольку текстовый файл – частный случай бинарного, этот метод вполне применим и к текстовому файлу.

Метод close()

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

Метод eof()

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

Метод seekg()

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

  • ios_base::end – Отсчитать новую позицию с конца файла
  • ios_base::beg – Отсчитать новую позицию с начала файла (абсолютное позиционирование)
  • ios_base::cur – Перескочить на n байт начиная от текущей позиции в файле (по умолчанию)
Ссылка на основную публикацию
Adblock detector