2016-05-04 3 views
3

Мне нужно написать класс в двоичный файл, а затем мне нужно его прочитать.C++ Чтение/запись класса из/в двоичный файл

У меня есть классы Triangle и BinaryFile и некоторые другие классы. Я не уверен, что неправильно пишу или читаю неправильно. При чтении происходит ошибка. После отладки я думаю, что он получает несоответствующие данные для моих личных переменных. Я буду очень рад, если кто-нибудь может дать мне несколько советов о том, как заставить его работать правильно.

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

https://my.pcloud.com/publink/show?code=XZJ7CYZbsLWLglqV5p83csijcEUTFqqpM3k

Я новичок в программировании и я не говорю по-английски очень хорошо, поэтому я заранее извиняюсь за свои ошибки.

class Point 
{ 
private: 
    int x; 
    int y; 
}; 

class Figure 
{ 
private: 
    string name; 
    string type; 
}; 

class Triangle: public Figure 
{ 
private: 
    Point p1, p2, p3; 
}; 

class BinaryFile 
{ 
private: 
    string FileName; 
    fstream File; 
public: 
    //... 
    void AddNewFigure(istream& stream) 
    {  
     File.open(this->FileName, ios::binary | ios::app); 
     if(!this->File) 
     { 
      cerr<<"File error <"<<this->FileName<<">\n"; 
      exit(1); 
     } 
     Triangle fig; 
     fig.MakeNewFigure(stream); 
     File.write((char*)&fig, sizeof(Triangle)); 
     File.close(); 
    } 

    Triangle GetTriangle() 
    { 
     Triangle trig; 
     Point p; 
     string str(""); int x(0); 
     File.open(this->FileName, ios::binary | ios::in); 
     if(!this->File) 
     { 
      cerr<<"File error <"<<this->FileName<<">\n"; 
      exit(1); 
     } 
     File.read((char*)&trig, sizeof(Triangle)); 
     File.close(); 
     return trig; 
    } 
}; 
+5

Будьте осторожны. Вы не можете написать структуру, содержащую std :: string, в файл, написав структуру. Он работает для int, потому что они хранятся в структуре, но std :: string имеет указатель на динамически выделенную память, которая хранится вне структуры. –

+0

хорошо, мне нужно изменить струны с чем-то еще? –

+0

Нет, вам просто нужна функция write() и read() в каждой структуре, которая записывает и считывает содержимое структуры из потока, потому что структура знает, являются ли ее члены хранятся в структуре (например, int) или вне структуры (например, станд :: строка). Я сделаю ответ ... –

ответ

0

Что вы ищете, это сериализовать ваши классы/данные, которые следует сохранить в файл. Для этого есть несколько библиотек, которые были оптимизированы в отношении потребления времени и памяти. Не могли бы вы использовать стороннюю библиотеку?

Если нет, посмотрите, например, boost serialization, cereal или, возможно, даже на Google ProtoBuf. Я понимаю, что Cereal - хороший старт, если вы используете C++ 11.

Если вы хотите написать собственную сериализацию, вам следует учитывать, что для каждого объекта с динамическим размером (например, строки) вам также необходимо сохранить размер объекта в файл. Для получения дополнительной информации, пожалуйста, посмотрите здесь:

https://stackoverflow.com/a/11003590/5874704

1

Это не легко воспроизвести ошибку, из-за ваш большой исходным код и отсутствующий файл данных. Но быстрый осмотр показывает, что вы читать и писать двоичные данные, используя блоковые операции:

Triangle trig; 
    ... 
    File.read((char*)&trig, sizeof(Triangle)); 

К сожалению, такой подход работает только тогда, когда объект, который вы хотите сохранить/нагрузки класса, который trivially copyable, как следующий код продемонстрирует:

if (is_trivially_copyable<Triangle>::value) 
    cout << "Triangle is trivially copyable" << endl; 
else cout << "Triangle is not trivially copyable" << endl; 

Таким образом, вы будете сериализовать поле записи содержимого объекта на поле вместо того, чтобы использовать операцию блока. Это FAQ on serialization должно помочь вам рассмотреть альтернативы.

0

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

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

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

#include <string> 
#include <fstream> 
#include <iostream> 

class Point 
{ 
private: 
    int x; 
    int y; 
public: 
    Point():x(0),y(0){} 
    Point(int x,int y):x(x),y(y){} 

    void write(std::ostream& f) 
    { 
     // We can just write out the bytes for x and y because 
     // they are primitive types stored in the class 
     f.write((char*)&x, sizeof(x)); 
     f.write((char*)&y, sizeof(y)); 
    } 
    void read(std::istream& f) 
    { 
     // We can just read the bytes directly into x and y because 
     // they are primitive types stored in the class 
     f.read((char*)&x, sizeof(x)); 
     f.read((char*)&y, sizeof(y)); 
    } 
}; 

class Figure 
{ 
private: 
    std::string name; 
    std::string type; 
public: 
    Figure(){} 
    Figure(std::string name,std::string type):name(name),type(type){} 

    void write(std::ostream& f) 
    { 
     size_t size; 

     // we need to store the data from the string along with the size 
     // because to restore it we need to temporarily read it somewhere 
     // before storing it in the std::string (istream::read() doesn't 
     // read directly to std::string) 

     size = name.size(); 
     f.write((char*)&size, sizeof(size_t)); 
     f.write((char*)name.c_str(), size); 

     size = type.size(); 
     f.write((char*)&size, sizeof(size_t)); 
     f.write((char*)type.c_str(), size); 
    } 
    void read(std::istream& f) 
    { 
     size_t size; 
     char *data; 

     // when we read the string data we need somewhere to store it 
     // because we std::string isn't a primitive type. So we read 
     // the size, allocate an array, read the data into the array, 
     // load the std::string, and delete the array 

     f.read((char*)&size, sizeof(size)); 
     data = new char[size+1]; 
     f.read(data, size); 
     data[size]='\0'; 
     name = data; 
     delete data; 

     f.read((char*)&size, sizeof(size)); 
     data = new char[size+1]; 
     f.read(data, size); 
     data[size]='\0'; 
     type = data; 
     delete data; 
    } 
}; 

class Triangle: public Figure 
{ 
private: 
    Point p1, p2, p3; 
public: 
    Triangle(){} 
    Triangle(Point x,Point y,Point z,Figure f):p1(x),p2(y),p3(z),Figure(f){} 


    void write(std::ostream& f) 
    { 
     // First write the base class then write the members of this class 
     Figure::write(f); 
     p1.write(f); 
     p2.write(f); 
     p3.write(f); 
    } 
    void read(std::istream& f) 
    { 
     // First read the base class then read the members of this class 
     Figure::read(f); 
     p1.read(f); 
     p2.read(f); 
     p3.read(f); 
    } 
}; 

class BinaryFile 
{ 
private: 
    std::string FileName; 
    std::fstream File; 
public: 
    BinaryFile(std::string FileName) : FileName(FileName) {}; 
    void WriteTriangle() 
    { 
     File.open(FileName, std::ios::binary | std::ios::out); 
     if(!File) 
     { 
      std::cerr<<"File error <"<<FileName<<">\n"; 
      exit(1); 
     } 
     Triangle trig({1,2},{3,4},{5,6},{"name","type"}); // something new 
     trig.write(File); 
     File.close(); 
    } 

    Triangle ReadTriangle() 
    { 
     File.open(FileName, std::ios::binary | std::ios::in); 
     if(!File) 
     { 
      std::cerr<<"File error <"<<FileName<<">\n"; 
      exit(1); 
     } 
     Triangle trig; // default values 
     trig.read(File); 
     File.close(); 
     return trig; 
    } 
}; 

main() 
{ 
    BinaryFile bin("file.bin"); 
    bin.WriteTriangle(); 
    Triangle trig = bin.ReadTriangle(); 
    // at this point trig has the values we stored 
    return 0; 
} 
+0

Я сделал это по-своему, и теперь он работает правильно. Большое спасибо за помощь! –