Как записать структуру в файл c
Я хочу записать объект структуры в файл, используя записывать() функция. Это должна быть эта функция.
Мой ввод с терминала: ./main.c output.dat Джон Доу 45
Когда я запускаю программу и открываю файл output.dat, появляется множество букв, которые не имеют смысла. Пожалуйста, помогите мне.
Вывод, который я хочу получить в моем файле output.dat: John Doe 45
Решение
Когда вы пишете структуру, вы только пишете значения в struct сам. В вашем случае эти значения являются указателями на другие места в памяти, а не на строковые данные. Таким образом, вы заканчиваете тем, что пишете адреса памяти в три указателя (12 или 24 байта в большинстве систем), которые не так уж полезны (так как они применяются к пространству памяти запущенной в данный момент программы, которое не будет таким же на следующий прогон).
Вам нужно будет разработать более полезный формат сериализации, который на самом деле записывает содержание из строк, а не их адреса. Варианты будут включать в себя простой перевод строки или NUL разделенный текст, текст с префиксом двоичной длины или сторонние библиотеки, чтобы получить правильные значения, CSV, JSON или XML (если вы чувствуете себя амбициозно, какая-то база данных).
Например, с префиксом текста двоичной длины вы можете сделать что-то вроде:
что позволяет вам читать его обратно, читая каждую длину строки (фиксированный размер), а затем используя ее, чтобы выяснить, сколько байтов нужно прочитать, чтобы получить строку.
Другие решения
Вы не можете просто выписать объект. Вам нужно выписать каждый из внутренних указателей в отдельности.
Что-то вроде этого:
Обратите внимание, я добавляю +1 на длину строк, чтобы убедиться, что я также выписать завершающий ноль.
Если вы знаете максимальную длину каждого поля, вы можете попробовать сделать поля массивом. Не забудьте добавить 1 для нулевого байта
Тогда ваш fwrite будет работать нормально. Но вам нужно ввести значения в структуру.
и так далее для каждого поля. strlcpy гарантирует, что ваша строка завершена нулем.
Спасибо всем, я сделал это так. Хотя он не идеален, он выполняет свою работу 🙂
Примеры использования средств C++ для работы с файлами
В теме приводятся примеры использования файловой системы C++ для:
- чтения информации из файлов;
- записи информации в файлы.
Содержание
- 1. Функция, читающая строки из клавиатуры и записывающая их в файл
- 2. Функция, которая читает текстовый файл и выводит его содержимое на экран
- 3. Пример бесформатного ввода/вывода. Копирование одного файла в другой
- 4. Пример бесформатного ввода/вывода. Копирование одного файла в другой. Функция put()
- 5. Пример функции записывающей структурную переменную в файл
- 6. Пример чтения структурной переменной из файла
- 7. Пример чтения/записи массива структур в файл. Функции write() , read()
- 8. Пример записи/чтения массива чисел типа double
- 9. Пример чтения из файла строк. Функция getline()
- 10. Пример чтения строк из файла. Функции getline() + eof()
Поиск на других ресурсах:
1. Функция, читающая строки из клавиатуры и записывающая их в файл
Результат работи программы:
2. Функция, которая читает текстовый файл и выводит его содержимое на экран
Функция Example2() читает содержимое файла filename , имя которого есть входящим параметром функции.
3. Пример бесформатного ввода/вывода. Копирование одного файла в другой
В примере реализована функция Example3() , которая выполняет копирование файлов в двоичном (бинарном) формате. Функция получает два параметра. Первый параметр типа const char* есть имя файла-источника. Второй параметр типа const char* есть имя файла-назначения.
Функция реализует посимвольное копирование. Для получения символа из файла-источника используется функция get() .
Вызов функции Example3() из функции main() может быть следующим:
4. Пример бесформатного ввода/вывода. Копирование одного файла в другой. Функция put()
Функция Example4() из данного примера работает также как и предыдущая, только вместо вывода в поток << используется функция put() . Также, с помощью функции is_open() выполняется проверка на корректность открытия файла.
5. Пример функции записывающей структурную переменную в файл
По данному примеру можно реализовывать собственные функции, которые будут записывать структуры или классы в файл.
Реализована функция Example5() , которая выполняет запись структурной переменной типа BOOK в файл, имя которого есть входящим параметром. Функция Example5() использует функцию write() для записи. Файл открывается в двоичном формате ( ios::binary ).
6. Пример чтения структурной переменной из файла
Данный пример есть продолжением предыдущего примера из пункта 5. В примере в функции Example6() заполняется значение структурной переменной типа BOOK . Полученное значение формируется как входящий параметр-ссылка на тип BOOK . Также функция получает параметром имя файла для чтения. Для чтения структурной переменной используется функция read() .
Результат работы программы
8. Пример записи/чтения массива чисел типа double
- запись в файл массива M чисел типа double функцией write() ;
- чтение из файла массива чисел типу double функцией read() .
Файл открывается в двоичном формате.
Результат работи программы:
9. Пример чтения из файла строк. Функция getline()
10. Пример чтения строк из файла. Функции getline() + eof()
В примере реализована функция Example10() , которая выполняет чтение строк из файла. Файл открывается в текстовом формате. Имя файла задается входящим параметром функции. Определение конца файла выполняется с помощью функции eof() .
До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в языке C реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.
В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:
С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения ("r"), записи ("w") или добавления ("a") и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:
Примечание. В случае использования относительной адресации текущим/рабочим каталогом в момент исполнения программы должен быть тот, относительно которого указанный относительный адрес корректен. Место нахождения самого исполняемого файла не важно.
При чтении или записи данных в файл обращение к нему осуществляется посредством файлового указателя (в данном случае, myfile).
Если в силу тех или иных причин (нет файла по указанному адресу, запрещен доступ к нему) функция fopen() не может открыть файл, то она возвращает NULL. В реальных программах почти всегда обрабатывают ошибку открытия файла в ветке if , мы же далее опустим это.
Объявление функции fopen() содержится в заголовочном файле stdio.h, поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE.
После того, как работа с файлом закончена, принято его закрывать, чтобы освободить буфер от данных и по другим причинам. Это особенно важно, если после работы с файлом программа продолжает выполняться. Разрыв связи между внешним файлом и указателем на него из программы выполняется с помощью функции fclose() . В качестве параметра ей передается указатель на файл:
В программе может быть открыт не один файл. В таком случае каждый файл должен быть связан со своим файловым указателем. Однако если программа сначала работает с одним файлом, потом закрывает его, то указатель можно использовать для открытия второго файла.
Чтение из текстового файла и запись в него
fscanf()
Функция fscanf() аналогична по смыслу функции scanf() , но в отличии от нее осуществляет форматированный ввод из файла, а не стандартного потока ввода. Функция fscanf() принимает параметры: файловый указатель, строку формата, адреса областей памяти для записи данных:
Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.
Допустим, у нас есть файл содержащий такое описание объектов:
Тогда, чтобы считать эти данные, мы можем написать такую программу:
В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.
fgets()
Функция fgets() аналогична функции gets() и осуществляет построчный ввод из файла. Один вызов fgets() позволят прочитать одну строку. При этом можно прочитать не всю строку, а лишь ее часть от начала. Параметры fgets() выглядят таким образом:
Такой вызов функции прочитает из файла, связанного с указателем myfile, одну строку текста полностью, если ее длина меньше 50 символов с учетом символа '\n', который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ '\0', добавленный fgets() . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет '\0'. В таком случае '\n' в считанной строке содержаться не будет.
В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr. Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.
getc() или fgetc()
Функция getc() или fgetc() (работает и то и другое) позволяет получить из файла очередной один символ.
Приведенный в качестве примера код выводит данные из файла на экран.
Запись в текстовый файл
Также как и ввод, вывод в файл может быть различным.
- Форматированный вывод. Функция fprintf ( файловый_указатель, строка_формата, переменные ) .
- Посточный вывод. Функция fputs ( строка, файловый_указатель ) .
- Посимвольный вывод. Функция fputc() или putc( символ, файловый_указатель ) .
Ниже приводятся примеры кода, в которых используются три способа вывода данных в файл.
Запись в каждую строку файла полей одной структуры:
Построчный вывод в файл ( fputs() , в отличие от puts() сама не помещает в конце строки '\n'):
Пример посимвольного вывода:
Чтение из двоичного файла и запись в него
С файлом можно работать не как с последовательностью символов, а как с последовательностью байтов. В принципе, с нетекстовыми файлами работать по-другому не возможно. Однако так можно читать и писать и в текстовые файлы. Преимущество такого способа доступа к файлу заключается в скорости чтения-записи: за одно обращение можно считать/записать существенный блок информации.
При открытии файла для двоичного доступа, вторым параметром функции fopen() является строка "rb" или "wb".
Тема о работе с двоичными файлами достаточно сложная, для ее изучения требуется отдельный урок. Здесь будут отмечены только особенности функций чтения-записи в файл, который рассматривается как поток байтов.
Функции fread() и fwrite() принимают в качестве параметров:
- адрес области памяти, куда данные записываются или откуда считываются,
- размер одного данного какого-либо типа,
- количество считываемых данных указанного размера,
- файловый указатель.
Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно "заказать" считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.
Пример использования функций fread() и fwrite() :
Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.
В этой статье есть несколько примеров, демонстрирующих различные способы записи текста в файл. В первых двух примерах используются удобные статические методы класса System.IO.File для записи каждого элемента любого объекта IEnumerable<string> и string в текстовый файл. В третьем примере показано, как добавить текст в файл, если необходимо обрабатывать отдельно каждую строку по мере ее записи в файл. В первых трех примерах происходит перезапись всего существующего содержимого файла. В последнем примере показано, как добавить текст в существующий файл.
Запись коллекции строк в файл
Предыдущий пример исходного кода:
Создает экземпляр массива строк с тремя значениями.
- Асинхронно создает имя файла WriteLines.txt. Если целевой файл уже существует, он будет перезаписан.
- Записывает заданные строки в файл.
- Закрывает файл, выполняет автоматическую очистку и удаление по мере необходимости.
Запись одной строки в файл
Предыдущий пример исходного кода:
Создает экземпляр строки с заданным назначенным строковым литералом.
- Асинхронно создает имя файла WriteText.txt. Если целевой файл уже существует, он будет перезаписан.
- Записывает заданный текст в файл.
- Закрывает файл, выполняет автоматическую очистку и удаление по мере необходимости.
Запись выбранных строк из массива в файл
Предыдущий пример исходного кода:
- Создает экземпляр массива строк с тремя значениями.
- Создает экземпляр StreamWriter с путем к файлу WriteLines2.txt как с помощью объявления.
- Выполняет итерацию по всем строкам.
- Условно ожидает вызов StreamWriter.WriteLineAsync(String), который записывает строку в файл, если строка не содержит "Second" .
Добавление текста в существующий файл
Предыдущий пример исходного кода:
- Создает экземпляр массива строк с тремя значениями.
- Создает экземпляр StreamWriter с путем к файлу WriteLines2.txt как с помощью объявления, передавая true для добавления.
- Ожидает вызов StreamWriter.WriteLineAsync(String), который записывает строку в файл как добавленную строку.
Исключения
При следующих условиях возможно возникновение исключения:
-
: Файл существует и является файлом только для чтения. : Имя пути имеет слишком большую длину. : Диск может быть переполнен.
Существуют дополнительные условия, которые могут привести к возникновению исключений при работе с файловой системой, поэтому программировать следует с осторожностью.
Для удобства обращения информация в запоминающих устройствах хранится в виде файлов.
Файл – именованная область внешней памяти, выделенная для хранения массива данных. Данные, содержащиеся в файлах, имеют самый разнообразный характер: программы на алгоритмическом или машинном языке; исходные данные для работы программ или результаты выполнения программ; произвольные тексты; графические изображения и т. п.
Каталог ( папка , директория ) – именованная совокупность байтов на носителе информации, содержащая название подкаталогов и файлов, используется в файловой системе для упрощения организации файлов.
Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются 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 байта – количество резервных секторов.
Кроме того, загрузочный сектор содержит следующую важную информацию:
- 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+" — открыть файл для чтения и дополнения, если файл не существует, то он создаётся.
Функция fclose() закрывает поток или потоки, связанные с открытыми при помощи функции fopen() файлами. Закрываемый поток определяется аргументом функции fclose() .
Возвращаемое значение: значение 0, если поток успешно закрыт; константа EOF , если произошла ошибка.
Чтение символа из файла:
Аргументом функции является указатель на поток типа FILE . Функция возвращает код считанного символа. Если достигнут конец файла или возникла ошибка, возвращается константа EOF .
Запись символа в файл:
Аргументами функции являются символ и указатель на поток типа FILE . Функция возвращает код считанного символа.
Функции fscanf() и fprintf() аналогичны функциям scanf() и printf() , но работают с файлами данных, и имеют первый аргумент — указатель на файл.
Функции fgets() и fputs() предназначены для ввода-вывода строк, они являются аналогами функций gets() и puts() для работы с файлами.
Копирует строку в поток с текущей позиции. Завершающий нуль- символ не копируется.
Пример Ввести число и сохранить его в файле s1.txt. Считать число из файла s1.txt, увеличить его на 3 и сохранить в файле s2.txt.
Читайте также: