Как записать структуру в файл в си
Перейти к содержимому

Как записать структуру в файл в си

Как записать структуру в файл в си

БлогNot. Структура со строками string и файловые чтение/запись массива таких структур

Структура со строками string и файловые чтение/запись массива таких структур

В отличие от этого примера, используем в структурном типе данных более удобные в обращении и современные строки string и файловые потоки вместо классических си-строк char * и файлов из <cstdio> . Лекции по всем этим темам можно найти в оглавлении.

В итоге должна получиться программка, которая покажет непосредственное задание значений полям структурной переменной и ввод значений полей с консоли, а также запись файла структур и последующее его контрольное чтение. Проверяться она будет в консоли Visual Studio 2015, проект создан как вот здесь.

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

Опишем структурный тип данных, включающий в себя идентификатор (номер) записи, две строки для хранения имени и даты рождения, а также вещественное поле money для хранения, например, зарплаты:

Мы не указываем какие-либо ограничения на длину строки, потому что сами будем управлять этим.

Так как программа может завершиться тремя способами — нормально, при ошибке записи файла и при ошибке его чтения, напишем функцию error с аргументом n (номер ошибки), которая будет за это отвечать. Ошибка номер ноль, как принято в C++, будет означать нормальное завершение. Вообще такую «общую точку выхода» зачастую полезно делать в процедурно-ориентированном коде:

Функция для ввода данных с консоли input получает аргументами адрес структуры a , куда нужно заносить данные (это может быть, в том числе, и адрес элемента массива структур) и идентификатор записи i , остальные поля записи она запрашивает у пользователя.

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

Также обратите внимание, что мы не должны «резать» поля структуры кодом вроде

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

Со всеми этими оговорками, простейшая функция ввода записи получилась такой:

Функция output , соответственно, выводит в консоль поля записи a , переданной аргументом:

При корректных данных должны получиться столбцы правильной ширины.

Главной программе осталось русифицировать консоль и создать массив записей, для простоты предусмотрим там всего 2 элемента:

Первый элемент с id , равным нулю, зададим программно, а второй введём с консоли:

Откроем файл notes.dat для записи и поместим туда данные, заодно печатая их в консоль:

Обратите внимание, что применение sizeof к составному объекту string некорректно, приходится писать в бинарный файл отдельными полями.

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

Вы понимаете, что наш файл — двоичный, и открывать его текстовым редактором бессмысленно.

Вот лог работы нашей программы:

скриншот 16-ричного вида файла (см. комменты)
скриншот 16-ричного вида файла (см. комменты)

  • писать и читать только string.c_str() в бинарные файлы, сведя задачу к предыдущей ссылке (но тогда не нужны и string , а можно делать классически на char * );
  • всё же отказаться от бинарных, а ограничиться текстовыми файлами и парсить полученные через getline строки;
  • предусмотреть в формате файла одновременное сохранение длины строковых данных объекта string .

В последнем случае имеем примерно такой подход (проверен не на структурах, а на паре строк).

Программа для записи набора string в бинарный файл:

Потом читаем это (возможно, с точностью до кодировки):

В моей консоли Visual Studio 2015 обе программки сработали. Итак, если строка string является полем структуры, разницы в подходах нет.

P.P.S. Ну и, раз пошла такая пьянка, теперь разрешим строкам string в массиве структур иметь произвольную длину и содержать пробелы (по умолчанию чтение string из файлового потока прервётся на первом пробельном символе), а сохранять всё будем в текстовом файле, и читать из него же.

Чтобы всё работало, нам пришлось переписать оператор >> для своей структуры.

Программа создаст и затем прочитает такой файл:

При чтении код сам избавится от лишних пробелов в строках между лексемами, следующими после числа.

Чтение / запись структуры в файл на C

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

    fwrite: ниже приводится объявление функции fwrite

// C программа для написания
// структура в файл
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// структура для чтения и записи

// открыть файл для записи

outfile = fopen ( «person.dat» , «w» );

if (outfile == NULL)

fprintf (stderr, «\nError opend file\n» );

// записать структуру в файл

fwrite (&input1, sizeof ( struct person), 1, outfile);

fwrite (&input2, sizeof ( struct person), 1, outfile);

printf ( «contents to file written successfully !\n» );

printf ( «error writing file !\n» );

// C программа для чтения
// структура из файла
#include <stdio.h>
#include <stdlib.h>

// структурировать человека с 3 полями

struct person input;

// Открыть person.dat для чтения

infile = fopen ( «person.dat» , «r» );

if (infile == NULL)

fprintf (stderr, «\nError opening file\n» );

// читать содержимое файла до конца файла

while ( fread (&input, sizeof ( struct person), 1, infile))

printf ( «id = %d name = %s %s\n» , input.id,

Эта статья предоставлена Мандипом Сингхом . Если вы как GeeksforGeeks и хотели бы внести свой вклад, вы также можете написать статью с помощью contribute.geeksforgeeks.org или по почте статьи contribute@geeksforgeeks.org. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

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

Ввод данных из файла и вывод в файл

До этого при вводе-выводе данных мы работали со стандартными потоками — клавиатурой и монитором. Теперь рассмотрим, как в языке 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() принимают в качестве параметров:

  1. адрес области памяти, куда данные записываются или откуда считываются,
  2. размер одного данного какого-либо типа,
  3. количество считываемых данных указанного размера,
  4. файловый указатель.

Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно «заказать» считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.

Пример использования функций fread() и fwrite() :

Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *