Problem z zapisem do pliku binarnego wektora obiektów posiadających wektor obiektów.

0

Witam, potrzebuje pomocy w moim projekcie aby zapisać obiekty do plików binarnych. Jest to projekt na studia i niestety mam trochę narzucone, że muszą to być pliki binarne.

Generalnie nie miałem problemów z takim zapisem i odczytem gdy miałem zapisać wektor obiektów, które nie są bardziej skomplikowane. Jednak problem się pojawia, gdy chce zapisać do pliku wektor obiektów ContactBook, które w sobie posiadają jeszcze wektor obiektów Contact. Metody zapisu i odczytu z pliku są w klasie ContactBookModel, w której jest też ten wektor najbardziej nadrzędny. Pliki nagłówkowe dla lepszego zobrazowania zamieszczę poniżej.

Poniżej wstawiam kod metod zapisu i odczytu z pliku binarnego:

Najpierw do pliku zapisuje rozmiar wektora książek, następnie zapisuje po kolei każdy obiekt książki. Podczas wczytywania danych analogicznie wczytuje rozmiar wektora, a później tworzę nowe obiekty książek i dodaje je do wektora. Problem pojawia się właśnie podczas samego odczytu kontaktów.

void ContactBookModel::saveDataToFile(std::string fileName)
{
    std::ofstream file(fileName, std::ios::binary | std::ios::trunc);

    if (!file.is_open()) {
        throw std::invalid_argument("Nie można otworzyć pliku");
    }

    size_t size = this->books.size();
    file.write(reinterpret_cast<char*>(&size), sizeof(size));

    for (auto& book : this->books) {
        file.write(reinterpret_cast<const char*>(&book), sizeof(book));
    }

    file.close();
}

void ContactBookModel::loadDataFromFile(std::string fileName)
{
    std::ifstream inputFile(fileName, std::ios::binary);

    if (!inputFile.is_open()) {
        throw std::invalid_argument("Nie można otworzyć pliku");
    }

    size_t numBooks;
    if (!inputFile.read(reinterpret_cast<char*>(&numBooks), sizeof(numBooks))) {
        throw std::invalid_argument("Błąd odczytu liczby obiektów!");
    }

    for (size_t i = 0; i < numBooks; i++) {
        ContactBook book;

        if (!inputFile.read(reinterpret_cast<char*>(&book), sizeof(book))) {
            throw std::invalid_argument("Błąd odczytu ksiazki!");
        }

        this->books.push_back(book);
    }

    inputFile.close();
}

Poniżej zamieszczam kod debugera, pokazujący obiekt book, od razu po odczytaniu do niego danych z pliku. Wartości zmiennych dla książek - name oraz desc są wczytane prawidłowo. Rozmiar wektora contacts też się zgadza. Problemem jest to, że nie wczytują się poprawnie kontakty.
Wydaje mi się, że z samym zapisem do pliku nie ma problemu.

image

Jak widać wartości nie udało się odczytać, co w rezultacie podczas dodawanie tego obiektu do wektora pojawia się wyjątek który zamieściłem poniżej:
image

image

// klasa ContactBookModel
#pragma once

#include <vector>
#include "ContactBook.h"
#include <fstream>

// Model
class ContactBookModel {
private:
    std::vector<ContactBook> books;

public:
    std::vector<std::string> getBooksVstr();

    void addBook(std::string name, std::string desc);
    bool deleteBook(int index);
    int getNumBooks();
    void saveDataToFile(std::string fileName);
    void loadDataFromFile(std::string fileName);
    ContactBook* getBook(int index);
};

// klasa Contact
#pragma once

#include <iostream>
#include <string>

class Contact
{
private:
	int id;
	int phone_number;
	std::string name;
	std::string last_name;
	std::string email;

public:
	Contact();
	Contact(int id, int phone_number, std::string name,
		std::string last_name, std::string email);

	void editContact(int phone_number, std::string name,
		std::string last_name, std::string email);

	void checkData(int phone_number, std::string name,
		std::string last_name, std::string email);

	void setId(int id);
	void setPhoneNumber(int phone_number);
	void setName(std::string name);
	void setLastName(std::string last_name);
	void setEmail(std::string email);

	int getId();
	int getPhoneNumber();
	std::string getName();
	std::string getLastName();
	std::string getEmail();
	std::string getContactStr();
};
// klasa ContactBook
#pragma once

#include "Contact.h"

#include <iostream>
#include <vector>
#include <string>

class ContactBook
{
private:
	std::string name;
	std::string desc;
	std::vector<Contact> contacts;

public:
	ContactBook();
	ContactBook(std::string name, std::string desc);
	~ContactBook();

	void editBook(std::string name, std::string desc);
	void checkData(std::string name, std::string desc);

	bool addContact(int phone_number, std::string name, std::string last_name, std::string email);

	bool removeContact(int id);
	
	Contact* getContact(int index);
	
	bool sortContacts();
	int getNumContacts();

	std::vector<Contact> getContacts();
	std::vector <std::pair<int, std::string>> getContactPairs();
	std::vector <std::string> getContactsVstr();
	std::vector <std::pair<int, std::string>> searchContacts(std::string input, std::string formula);
	
	std::string getName();
	std::string getDesc();
	void setName(std::string name);
	void setDesc(std::string desc);
};

To samo rozwiązanie można powiedzieć, że działa na moim przykładowym projekcie. Gdzie robię to samo ale w trochę prostszej formie. Zamieszczam link do kodu na pastebin: https://pastebin.com/h7zhnuXz. Jedynym problem w tym kodzie było to, że wyskakiwał wyjątek, tyle, że podczas punktu return 0 w main.

Czy robiąc tym sposobem ma to w ogóle prawo działać? Może problem leży jeszcze w innej części programu?
Liczę na waszą pomoc w rozwiązaniu problemu.

@Edit Dodam jeszcze, że jak śledzę w debugerze zmienne, które zapisuje do pliku, to mają one poprawne wartości.

3

Nie możesz reprezentacji binarnej obiektów zapisywać bezpośrednio. Poczytaj o serializacji. To zdecydowanie overkill, ale możesz popatrzeć np. na Protocol Buffers.

Przykłady:

0
Zigor36 napisał(a):

Generalnie nie miałem problemów z takim zapisem i odczytem gdy miałem zapisać wektor obiektów, które nie są bardziej skomplikowane.

Sądzę że nie było dobrze, najwyżej nie wykazałeś sam sobie błędów

@Edit Dodam jeszcze, że jak śledzę w debugerze zmienne, które zapisuje do pliku, to mają one poprawne wartości.

Zmienna C/C++ to "tylko" obszar pamieci. Zapis ok, ALE jej ODCZYT, ex. przydkowego obszaru pamięci utrwalonego w pliku, do nowego obszaru, nie może być dobrze.Zgadzam sie kq, nie zrozumiałeś jeszcze tego.

ps. korekta. Nie każdy zapis się "uda". Jeśli zmienną jest coś o sensie wskaznika, zapisywany jest adres, w sposób oczywisty bezużyteczny póżniej. Ale prawdą jest, że przy odczycie może byc tylko gorzej.

0

Szczęście początkującego, akurat metoda write przyjmuje wskaźnik, a tak się składa, że wectora pierwszy element to wskaźnik na tą właśnie strukturę, która jest ciągła, pewnie dlatego mu to działało.

0
TheWypierdzisty napisał(a):

Szczęście początkującego, akurat metoda write przyjmuje wskaźnik, a tak się składa, że wectora pierwszy element to wskaźnik na tą właśnie strukturę, która jest ciągła, pewnie dlatego mu to działało.

Tak, taka myląca "przychylna" zbieżność - obiecujaca ze w przyszłosći bedzie równie dobrze.

1 użytkowników online, w tym zalogowanych: 0, gości: 1