Перейти к содержимому

Как записать структуру в бинарный файл c

  • автор:

Как записать сразу всю структуру (содержащую string) в бинарный файл?

Структуру необходимо записать таким методом, чтобы потом запустить отдельно другую функцию, которая сможет прочитать файл, но перед этим не добавлять записи в файл(ну то есть чисто чтение)
Пробовала так: после того как пользователь введет string посчитать string.size() + 1 и приплюсовать sizeof(int), но после чтение из файла выдает какой-то бред

  • Вопрос задан более трёх лет назад
  • 4582 просмотра

15 комментариев

Средний 15 комментариев

justAnotherCluelessUser @justAnotherCluelessUser

«но после чтение из файла выдает какой-то бред»
Почему так много людей верят в ясновидящих??

Тут их особо нет. вроде.
Так что весь код на текстообменник(типа пастбин) и ссылку.

SUkI3 @SUkI3 Автор вопроса

anketa ar; int size = 0; int k = 0; fstream fout ("test.dat", ios::binary | ios::app); if(!fout) < coutcout> k; for(int i = 0; i> ar.year; cin.ignore(); cout fout.close();

Потом вот это запускаю отдельно

fstream file("test.dat", ios::binary | ios::in); if(!file) < coutif (getline( file, ar.fio, '\0' )) do < cout while(getline( file, ar.fio, '\n' )); file.close();

RabraBabr

Вы понимаете, что тут происходит?

size = sizeof(ar.year) + ar.fio.length() + 1; fout.write((char*)&ar,sizeof(size));

SUkI3 @SUkI3 Автор вопроса
RabraBabr, передаем байты и размер, который получился после как пользователь ввел данные

RabraBabr

SUkI3, какие байты? И размер чего? Внимательно подумайте.
SUkI3 @SUkI3 Автор вопроса

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

Алексей Сергей @dev_random

SUkI3, почитайте про POD типы. В приведенном коде std::string не является POD типом и соответсвенно вся anketa тоже. При fout.write(&ar, sizeof(ar)) в файл попадут внутренние поля std::string (включая указатель (адрес) на строку), но не сама строка!

SUkI3 @SUkI3 Автор вопроса

Алексей Сергей, да я знаю, что структура не POD, если б было все так просто.
Поэтому и вопрос на тостере
Так возможно создать алгоритм который записывает структуру со string так, как если б там не было string, а были только POD типы?

Алексей Сергей @dev_random

SUkI3, смотрите в сторону сериализаторов.
Есть много либ на гитхабе. Например я такую пару раз использовал: https://github.com/USCiLab/cereal
Есть еще либа в бусте, но не все хотят буст в зависимости тащить. https://www.boost.org/doc/libs/1_70_0/libs/seriali.

Алексей Сергей @dev_random

SUkI3, забыл еще упомянуть, что сериализаторы обычно предоставляют несколько форматов для выходного файла. Легко делается как-минимум: бинарный, бинарный-кроссплатформа, текст, json и xml.

SUkI3 @SUkI3 Автор вопроса
Алексей Сергей, сериализаторы какое страшное слово, спасибо, почитаем)

RabraBabr

myjcom

Roman @myjcom Куратор тега C++
SUkI3, в чем смысл бинарной записи обычной строки. Байт на байт, шило на мыло.
SUkI3 @SUkI3 Автор вопроса
Roman, у string методы, модификаторы есть уже готовые, вроде как легче работаться должно

myjcom

Roman @myjcom Куратор тега C++

SUkI3,
при чем тут сейчас методы и модификаторы?
вы данные (data) сохраняете, данные в std::string это динамический массив char.
если использовать

struct anketa < int year; char fio[64]; >;

то все гораздо проще и с чтением и с записью.

Со string как раз таки мороки больше.

#include #include #include #include using namespace std; struct Data < int value = 0; string str; >; ofstream& operator<<(ofstream& ofs, Data& d) < size_t sz = d.str.size(); ofs.write(reinterpret_cast(&d.value), sizeof(d.value)); ofs.write(reinterpret_cast(&sz), sizeof(sz)); ofs.write(d.str.c_str(), sz); return ofs; > ifstream& operator>>(ifstream& ifs, Data& d) < ifs.read(reinterpret_cast(&d.value), sizeof(d.value)); size_t sz; ifs.read(reinterpret_cast(&sz), sizeof(sz)); d.str.resize(sz); ifs.read(reinterpret_cast(&*d.str.begin()), sz); return ifs; > int main() < Data d = ; ofstream ofs("D:\\file.bin", ios_base::binary); ofs > d2; assert(d.value == d2.value && d.str == d2.str); cin.get(); >

Запись структуры в бинарный файл и чтение из него

Здравствуйте! Помогите решить проблему! У нас есть структура data_count, в которой хранятся полученные параметры файлов типа .doc. Бинарный файл должен являтся некой "базой данных", в котором должны хранится записи с этими параметрами.

struct data_count
long int W;
long int C;
long int Pg;
long int Par;
long int Ln;
long int CWS;
>;
data_count X[10000];

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

//---получение количества записей в файле---------------------------

Далее делаем сверку новых полученных параметров с теми, которые уже записаны в файл. Если совпадающих параметров нет - записываем структуру с новыми данными в файл.

. MessageBox(NULL, "Файл успешно принят", "Статус проверки", MB_OKCANCEL);
X[quantity].W = WCount;
X[quantity].C = CCount;
X[quantity].Pg = PgCount;
X[quantity].Par = ParCount;
X[quantity].Ln = LnCount;
X[quantity].CWS = CWSCount;

Не пойму почему, но всё отлично работает только с первыми двумя записями: и проверяет, и сверяет, и пишет в файл. После получения параметров 3-го документа структра записывается на место второй записи в файле (переменная quantity получает значение 2, а не 3). Элемент X.W, уже записанной в бинарный файл структры, никогда не будет нулевым, т.к показывает количество слов в файле .doc (этот момент в программе предусмотрен). Помогите разобраться, как структуру с новыми полученными данными записать после последней записи в файле. заранее спасибо.

Как записать структуру в бинарный файл c

Хотя функции getc()/putc() позволяют вносить в файл отдельные символы, но фактически мы имеем дело с бинарными файлами. Если мы записываем в файл строку, то в принципе мы даже можем открыть записанный файл любом текстовом редакторе и понять, что там было записано. Но не всегда данные могут представлять строки. И чтобы более наглядно разобраться с работой с бинарными файлами, рассмотрим еще одни пример - с записью-чтением структуры из файла.

#include #include struct person < char name[16]; int age; >; int save(char * filename, struct person *p); int load(char * filename); int main(void) < char * filename = "person.dat"; struct person tom = < "Tom Smith", 21 >; save(filename, &tom); load(filename); return 0; > // запись структуры в файл int save(char * filename, struct person *p) < FILE * fp; char *c; int size = sizeof(struct person); // количество записываемых байтов fp = fopen(filename, "wb"); //открываем файл для бинарной записи if (!fp) // если не удалось открыть файл < printf("Error occured while opening file \n"); return 1; >// устанавливаем указатель на начало структуры c = (char *)p; // посимвольно записываем в файл структуру for (int i = 0; i < size; i++) < putc(*c++, fp); >fclose(fp); return 0; > // загрузка из файла структуры int load(char * filename) < FILE * fp; char *c; int i; // для считывания одного символа // количество считываемых байтов int size = sizeof(struct person); // выделяем память для считываемой структуры struct person * ptr = malloc(size); fp = fopen(filename, "rb"); // открываем файл для бинарного чтения if (!fp) < printf("Error occured while opening file \n"); return 1; >// устанавливаем указатель на начало блока выделенной памяти c = (char *)ptr; // считываем посимвольно из файла while ((i = getc(fp))!=EOF) < *c = i; c++; >fclose(fp); // вывод на консоль загруженной структуры printf("%-20s %5d \n", ptr->name, ptr->age); free(ptr); return 0; >

В данном случае запись и чтение структуры выделены в отдельные функции: save() и load() соответственно.

Для записи в функции save() через параметр struct person *p получаем указатель на сохраняемую структур. Фактически его значением является начальный адрес блока памяти, где располагается структура.

Функция putc записывает отдельный символ в файл, однако нам надо записать структуру. Для этого мы создаем указатель на символ (который по сути представляет один байт) и устанавливаем этот указатель на начало блока памяти, выделенного для структуры.

c = (char *)p;

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

for (int i = 0; i

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

При чтении файла в функции load() используется похожий принцип только в обратную сторону.

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

struct person * ptr = (struct person *) malloc(size);

После этого указатель ptr будет указывать на первый адрес блока из 20 байт (наша структура занимает 20 байт = 16 символов и 4 байта для числа int ).

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

c = (char *)ptr; // считываем посимвольно из файла while ((i = getc(fp))!=EOF)

Здесь стоит обратить внимание на то, что в данном случае на самом деле считываем даже не символ, а числовой код символа в переменную типа int и только потом передаем значение указателю c. Это сделано для корректной обработки окончания файла EOF. Это значение может представлять любое отрицательное число. И если бы мы сохранили отрицательное число (например, возраст пользователя был бы отрицательным), то оно было бы некорректно интерпретировано при чтении как конец файла, и итоговый результа был бы неопределенным. Поэтому более правильно считывать именно числовой код символа в переменную int, а затем числовой код передавать в char.

Запись и чтение массива структур

Выше приведен пример по работе с одной структурой. Но, как правило, при работе с файлами мы оперируем не одной структурой, а каким-то набором структур. Поэтому усложним задачу и сохраним и считаем из файла массив структур:

#include #include struct person < char name[20]; int age; >; int save(char * filename, struct person *st, int n); int load(char * filename); int main(void) < char * filename = "people.dat"; struct person people[] = < , , , >; int n = sizeof(people) / sizeof(people[0]); save(filename, people, n); load(filename); return 0; > // запись в файл массива структур int save(char * filename, struct person * st, int n) < char *c; // для записи отдельных символов // число записываемых байтов int size = n * sizeof(struct person); FILE * fp = fopen(filename, "wb"); if (!fp) < printf("Error occured while opening file\n"); return -1; >// записываем количество структур c = (char *)&n; for (int i = 0; i < sizeof(n); i++) < putc(*c++, fp); >// посимвольно записываем в файл все структуры c = (char *)st; for (int i = 0; i < size; i++) < putc(*c, fp); c++; >fclose(fp); return 0; > // загрузка из файла массива структур int load(char * filename) < char *c; // для считывания отдельных символов int m = sizeof(int); // сколько надо считать для получения размера массива int n; // количество структур в массиве FILE * fp = fopen(filename, "r"); if (!fp) < printf("Error occured while opening file\n"); return -1; >// выделяем память для хранения количества данных int *ptr_count = malloc(m); // считываем количество структур c = (char *)ptr_count; // пока не считаем m байт, сохраняем байт в выделенный блок для размера массива while (m > 0 && (*c = getc(fp)) != EOF) < c++; m--; >//получаем число элементов n = *ptr_count; free(ptr_count); // освобождаем память // выделяем память для считанного массива структур struct person * ptr = malloc(n * sizeof(struct person)); // устанавливаем указатель на блок памяти, выделенный для массива структур c = (char *)ptr; // считываем посимвольно из файла while ((*c= getc(fp))!=EOF) < c++; >// перебор загруженных элементов и вывод на консоль printf("\nFound %d people\n\n", n); for (int i = 0; i < n; i++) < printf("%-5d %-10s %5d \n", i + 1, (ptr + i)->name, (ptr + i)->age); // или так // printf("%-5d %-10s %5d \n", i + 1, ptr[i].name, ptr[i].age); > free(ptr); fclose(fp); return 0; >

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

Запись во многом аналогична записи одной структуры. Сначала устанавливаем указатель на число n , которое представляет количество структур, и все байты этого числа записываем в файл:

c = (char *)&n; for (int i = 0; i

Затем подобным образом записываем все байты из массива структур - устанавливаем указатель на первый байт массива структур и записываем size байт в файл:

// посимвольно записываем в файл все структуры c = (char *)st; for (int i = 0; i

При чтении нам придется файктически считывать из файла два значения: количество структур и их массив. Поэтому при чтении два раза выделяется память. Вначале для количества элементов:

int *ptr_count = malloc(m);

Затем мы считываем первые 4 байта из файла для получения числа:

c = (char *)ptr_count; while (m > 0 && (*c = getc(fp)) != EOF) < c++; m--; >//получаем число элементов n = *ptr_count;

Затем аналогичные действия проделываем для массива структур.

struct person * ptr = malloc(n * sizeof(struct person)); // устанавливаем указатель на блок памяти, выделенный для массива структур c = (char *)ptr; // считываем посимвольно из файла while ((*c= getc(fp))!=EOF)

И результатом программы должен быть вывод считанных данных:

Found 4 people 1 Tom 23 2 Alice 27 3 Bob 31 4 Kate 29

Как записать, а после прочитать из бинарного файла структуру?

Суть такая, пишу аля "Архиватор", суть такая, имеется структура:

struct File

После чего начинаю обрабатывать файл, который вводит пользователь:
P.S.: Кст, да, пишу на Си под Linux.>

5e9cbba5029bc098161653.png

Я определяю размер файла, который на вход, после чего выделяю память для ПЕРЕМЕННОЙ структуры, чтобы потом fread в эту переменную содержимое файла, сохраняю всю структуру в файл. Почему-то бинарный файл меньше исходного, хотя там структура. Исх. файл 13 байт "Hello world!", а бинарный где-то 6-7 байт получается, не понимаю почему так.

И как потом прочитать эту структуру, если я выделял память отдельно еще под содержимое?
Пробовал выделить для структуры память в видеstruct File *test = malloc(struct File) и прочитать файл fread(test, РАЗМЕР_ФАЙЛА, 1, ФАЙЛ), но выдает ошибку, не могу разобраться, как прочитать файл? Но у меня еще есть ощущение, что я неправильно делаю запись в бинарник, насчет того, что выделяю память отдельно для переменной еще.

  • Вопрос задан более трёх лет назад
  • 499 просмотров

4 комментария

Простой 4 комментария

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

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