Как записать и считать структуру в файл на СИ
Всем добрый вечер, такая проблема, записываю структуру в файл, но при следующем запуске и чтении из файла, программа перестает работать. Также при удаление строчки 114 и 115 программа перестает работать, может кто подсказать почему?
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdio>
using namespace std;
typedef struct _date {
int num;
char* month;
int year;
} date;
typedef struct _fio {
char* fam; //Фамилия
char* name; //Имя
char* father; //отчество
} fio;
struct _deposid {
int countName;
fio client;
date date;
double sum;
};
// Функция вывода счета
void printClient(_deposid &client) {
cout << client.countName << "\t" << client.client.name << "\t" << client.client.fam << "\t" << client.client.father << "\t" << client.date.num << ' ' << client.date.month << ' ' << client.date.year << "\t" << client.sum << endl;
}
void sumLarge(_deposid *clients, int thisClient, int needSum, int countSum) {
for (int i = 0; i < thisClient; i++ ) {
if (clients[i].sum > needSum) {
printClient(clients[i]);
countSum++;
}
}
if (countSum == 0) {
cout << "The list is empty\n";
}
countSum = 0;
}
void printNameLastname(_deposid *clients, int thisClient, char *firstname, char *lastname, char *father, int countSum) {
for (int i = 0; i < thisClient; i++ ) {
if (strcmp(clients[i].client.name, firstname) == 0 && strcmp(clients[i].client.fam, lastname) == 0 && strcmp(clients[i].client.father, father) == 0) {
printClient(clients[i]);
countSum++;
}
}
if (countSum == 0) {
cout << "The list is empty\n";
}
countSum = 0;
}
// Функция создания нового клиента
void newClient(_deposid &client) {
char *userLastname = new char[100], *userFirstname = new char[100], *userFather = new char[100], *userMonth = new char[100];
// Ввод данных
cout << "\nInput firstname: ";
cin >> userFirstname;
client.client.name = new char[strlen(userFirstname)+1];
strcpy(client.client.name, userFirstname);
cout << "Input lastname: ";
cin >> userLastname;
client.client.fam = new char[strlen(userLastname)+1];
strcpy(client.client.fam, userLastname);
cout << "Input middlename: ";
cin >> userFather;
client.client.father = new char[strlen(userFather)+1];
strcpy(client.client.father, userFather);
cout << "Input date: ";
cin >> client.date.num;
cout << "Input month: ";
cin >> userMonth;
client.date.month = new char[strlen(userMonth)+1];
strcpy(client.date.month, userMonth);
cout << "Input year: ";
cin >> client.date.year;
cout << "\nInput sum on check: ";
cin >> client.sum;
// Очистка памяти
delete [] userLastname;
delete [] userFirstname;
}
int main() {
char *lastname = new char[100], *firstname = new char[100], *father = new char[100];
int n, thisClient = 0, numberFunction = 0, needSum, countSum = 0;
_deposid *clients;
char c;
FILE *fp;
fp=fopen("t.txt", "r+");
if (fp==NULL) {
fclose(fp);
fp=fopen("t.txt", "w+");
cout << "Input amount clients: ";
cin >> n;
clients = new _deposid[n]; // Создание массива структур
}
else {
while((c = fgetc(fp)) != EOF) // При удалении этой и строчки ниже программа вообще не запускается
putchar(c);
while(fread(clients, sizeof(_deposid),1,fp));
// fputs("lol", fp);
// fclose(fp);
}
cout << "\n------------------------------------------------\n";
cout << "Input new check: 1\nOutput all check: 2\nFind all accounts with an amount greater than a given one: 3\nFind all accounts of a given customer (by first and last name): 4\nSave and exit: 5\n";
while (numberFunction != 5) {
cout << "------------------------------------------------";
cout << "\nWhat to do: ";
cin >> numberFunction;
switch (numberFunction) {
case 1:
if (thisClient == n) {
cout << "\nAdded maximum number of clients";
} else {
clients[thisClient].countName = thisClient + 1;
newClient(clients[thisClient]);
cout << "\n------------------------------------------------\n";
printClient(clients[thisClient]);
thisClient++;
}
break;
case 2:
cout << "All check: \n";
for (int i = 0; i < thisClient; i++ ) {
printClient(clients[i]);
}
break;
case 3: // Вывод счетов с суммой большей заданной
cout << "Enter a number to sort: ";
cin >> needSum;
cout << "------------------------------------------------\n";
cout << "Accounts with an amount greater than the specified: \n";
sumLarge(clients, thisClient, needSum, countSum);
break;
case 4:
cout << "Input firstname: ";
cin >> firstname;
cout << "\nInput lastname: ";
cin >> lastname;
cout << "\nInput middlename: ";
cin >> father;
cout << "------------------------------------------------\n";
cout << "Target customer accounts: \n";
printNameLastname(clients, thisClient, firstname, lastname, father, countSum);
break;
case 5:
cout << "\nSave and exit...";
// fp=fopen("t.txt","wb");
for(int i=0;i<n;i++)
fwrite(&clients, sizeof(_deposid), 1, fp);
fclose(fp);
}
}
}
Ответы (1 шт):
В принципе всё просто. Вы в файл сохраняете не все данные.
case 5:
cout << "\nSave and exit...";
// fp=fopen("t.txt","wb");
for(int i=0;i<n;i++)
fwrite(&clients, sizeof(_deposid), 1, fp);
fclose(fp);
Когда вы так пишете, в файл сохраняется сама структура. Т.е. в файл сохраняются не текстовые данные, кот. вы записывали в выделяемые через new буферы, а просто значение указателей char* month; и char* fam; char* name; char* father; . Для примера, проверьте значение sizeof(_deposid) - в размере структуры не будет размера того текста (фамилии, имени, отчества, названия месяца), которые вы вводили с клавиатуры.
А потом Вы загружаете из файла значение указателей, которые указывают в "никуда".
Вам нужно написать свою функцию вывода Вашей структуры в файл. И свою функцию загрузки структуры из файла.
Существуют уже готовые библиотеки сериализации (вывода в поток и загрузки из потоков) объектов. Но для этой задачи проще написать свои функции.
Совет - каждое поле пишите отдельной строкой и считывайте построчно - так будет проще.
Ну и вот этот кусок:
fp=fopen("t.txt", "r+");
if (fp==NULL) {
clients = new _deposid[n]; // Создание массива структур
}
else {
while((c = fgetc(fp)) != EOF) // При удалении этой и строчки ниже программа вообще не запускается
putchar(c);
while(fread(clients, sizeof(_deposid),1,fp));
Как правильно написал @JohnDoe выделение памяти под массив структур делается только если файла не существует. Если он существует, память под массив не выделяется. И дальше у Вас 2 ошибки, первая из которых не дает состояться второй. Вторая ошибка - запись данных в невыделенную область памяти:
fread(clients, sizeof(_deposid),1,fp)
Здесь для clients память не выделена! А Вы пытаетесь записать туда что-то!
А первая ошибка не дает случиться второй - вот этими строками вы считываете весь файл.
while((c = fgetc(fp)) != EOF)
putchar(c);
Дескриптор файла указывает на конец файла. Функция fread() не может ничего считать из файла и поэтому ничего не записывает в невыделенную память.
По вашей логике при записи массива структур в файл вам нужно сначала записать туда количество объектов в массиве, а потом - сами объекты. И считывать в таком же порядке:
- считать количество объектов,
- выделить под них память,
- считать сами объекты
Ещё один момент:
void newClient(_deposid &client) {
char *userLastname = new char[100],
*userFirstname = new char[100],
*userFather = new char[100],
*userMonth = new char[100];
......
delete [] userLastname;
delete [] userFirstname;
Выделили 4 массива, а освободили только 2. А в main() выделили и вообще не освободили. И зачем выделение через new(), если вы знаете размер буферов? Только чтобы память на куче была выделена? Стек выдержит ваши 400 байт, сделайте проще:
void newClient(_deposid &client)
{
char userLastname[100],
userFirstname[100],
userFather[100],
userMonth[100];
}
