1

我想将文件读入结构或类,但经过一番阅读后,我发现这样做不是一个好主意:

int MyClass::loadFile( const char *filePath ) {

            ifstream file ( filePath, ios::in | ios::binary );

            file.read ((char*)this, 18);

            file.close();

            return 0;

        }

我猜如果我想从一个结构/类中写一个文件,这也不是 kosher:

void MyClass::writeFile( string fileName ) {

        ofstream file( fileName, ofstream::binary ); 

        file.write((char*)this, 18);

        file.close();

    }

听起来我不想这样做的原因是因为即使我的结构的数据成员加起来最多 18 个字节,其中一些可能会在内存中填充额外的字节。有没有更正确/优雅的方式将文件放入这样的类/结构中?

4

2 回答 2

3

首选的通用技术称为序列化。

它比二进制表示更脆弱。但它有需要解释的开销。标准类型与序列化配合得很好,鼓励您使您的类序列化,以便包含您的类的类可以轻松地序列化。

class MyClass {
     int x;
     float y;
     double z;
     friend std::ostream& operator<<(std::ostream& s, MyClass const& data);
     friend std::istream& operator>>(std::istream& s, MyClass& data);
};

std::ostream& operator<<(std::ostream& s, MyClass const& data)
{
    // Something like this
    // Be careful with strings (the input>> and output << are not symmetric unlike other types)
    return str << data.x << " " << data.y << " " << data.z << " ";
}

// The read should be able to read the version printed using <<
std::istream& operator>>(std::istream& s, MyClass& data)
{
    // Something like this
    // Be careful with strings.
    return str >> data.x >> data.y >> data.z;
}

用法:

int main()
{
    MyClass   plop;
    std::cout << plop;  // write to a file
    std::cin  >> plop;  // read from a file.


    std::vector<MyClass>  data;

    // Read a file with multiple objects into a vector.
    std::ifstream  loadFrom("plop");
    std::copy(std::istream_iterator<MyClass>(loadFrom), std::istream_iterator<MyClass>(),
              std::back_inserter(data)
             );


    // Write a vector of objects to a file.
    std::ofstream   saveTo("Plip");
    std::copy(data.begin(), data.end(), std::ostream_iterator<MyClass>(saveTo));

    // Note: The stream iterators (std::istream_iterator) and (std::ostream_iterator)
    //       are templatized on your type. They use the stream operators (operator>>)
    //       and (operator<<) to read from the stream.
}
于 2013-07-26T23:44:47.920 回答
0

答案是:这个问题没有灵丹妙药。

您可以消除填充以确保使用类中的数据成员的一种方法(在您正在使用的 MSVC 中)

#pragma pack( push, 1 )

class YourClass {
    // your data members here
    int Data1;
    char Data2;
    // etc...
};

#pragma pack( pop )

这种方法的主要用处是您的类是否匹配预定义的格式,例如位图标题。如果它是代表猫、狗等的通用类,则不要使用这种方法。如果这样做是为了确保您知道编译器数据类型的字节长度,如果您的代码永远是多平台的,那么您应该为成员使用显式大小,例如 __int32 等。

如果这是一个通用类,那么在您的保存成员中,每个值都应该明确写入。这样做的一个技巧是创建或从 sourceforge 或其他地方获取好的代码来帮助做到这一点。理想情况下,一些允许命名成员的代码,我使用类似于:

SET_WRITE_DOUBLE( L"NameOfThing", DoubleMemberOfClass );
SET_WRITE_INT( L"NameOfThing2", IntMemberOfClass );
// and so on...

我创建了这些宏背后的代码,我现在不分享这些代码,但是聪明的人可以创建自己的代码来将命名保存到无序集中的流中。我发现这是一种完美的方法,因为如果您在类中添加或减去数据成员,则保存/加载不依赖于保存的二进制表示和顺序,因为如果您按顺序保存,您的类无疑会随着时间的推移而演变是您将面临的问题。

我希望这有帮助。

于 2013-07-26T23:34:28.973 回答