从 .bin 文件中读取二进制数据到 C++ 中的结构

Reading binary data from a .bin file into structs in C++

我有一组 .bin 文件,其中包含正式指定格式的数据。我确切地知道每个字段有多少字节,例如名称 = 40 字节,版本号 = 2 字节等。 我也知道它们存储在文件中的确切顺序(例如名称,然后是版本号....)。

到目前为止,我可以将文件中的数据加载到 std::vector<unsigned char> 列表中,然后遍历该数据并根据预期字节数读取字段。

问题是这个方法很长而且容易出错,如果我弄错了任何字段(有很多不同的字段)。

我看过并与人们讨论过结构打包、指针转换和位域。我似乎无法让他们一起工作。

如何将数据读入我的缓冲区,然后 'overlay' 我的结构在缓冲区中?然后所有字段将根据分配的位字段填充,我在结构中给了每个值。

位字段的问题是我不能接受字符串。

建议或示例代码将不胜感激。如果你只想发表评论,我可以给你代码来展示我目前所拥有的以及我正在努力实现的目标。

#include <vector>

int main()
{
    //File data loaded by function call
    std::vector<unsigned char> fileData;

    //How do I cast fileData to be a dataFields type? 
}

struct dataFields 
{
    int ID : 8;
    // Cannot use bit field for string type? 
    std::string name;
    int versionNumber : 16;
    int someOtherValue : 8;
}

由于工作原因,我无法给出我正在处理的确切代码,但我觉得这总结了我在一个简单的庄园中试图做得相当好的事情。

不,您确实不能为 std::string 使用位模式,因为它只包含一些指针。

我在项目中使用的常用方法是为每种记录类型设置 POD 结构。 然后负责{de}序列化的最低层只在PODs和字节之间转换。任何 C++ 逻辑,如 std::string 或可变长度 std::vector 都在更高级别处理。

#include <array>
#include <type_traits>
#include <cstdint>
#include <cstring>

struct Record{
    std::uint8_t ID;
    std::array<char,40> name;
    std::uint16_t versionNumber;
    std::uint8_t someOtherValue;
};

static_assert(sizeof(Record)==46);
static_assert(offsetof(Record,name)==1);

在我的世界里,我尝试让 Record 尊重每个元素对 sizeof(E) 的标准对齐。如果需要,您可以添加压缩修饰符。在位域之前优先使用 <cstdint> 中的类型。

我建议在每个 Record 之后放置一堆 static_assert,以验证其布局。否则有一天会有人出现并试图“清理”代码,破坏一切。它还很好地记录了 reader.

的协议

一个缺点是这不容易支持将可变长度成员放在中间或有多个成员,但我从来不需要这样做,保持数据包简单。

另外,我只是决定协议的固定字节顺序。如果有人需要其他东西,他们有责任传递正确编码的 Records 进行序列化。

序列化助手:

template<typename T>
T read_value(const unsigned char*& ptr){
    static_assert(std::is_standard_layout_v<T>);

    T value;
    std::memcpy(&value,ptr,sizeof(T));
    ptr+=sizeof(T);
    return value;
}

template<typename T>
void write_value(unsigned char*& ptr, const T& value){
    static_assert(std::is_standard_layout_v<T>);

    std::memcpy(ptr,&value,sizeof(T));
    ptr+=sizeof(T);
}

负责{反}序列化的最低层看起来像这样:

void deserialize_stream(const unsigned char* bytes){\
    // Output is bunch of POD types.
    auto record1 = read_value<Record>(bytes);
    auto record2 = read_value<Record>(bytes);
}

void serialize_stream(unsigned char* bytes){
    // Input is a list of POD types to serialize.
    Record record1{1,"Foo",12,42};
    Record record2{2,"Bar",14,28};

    write_value(bytes,record1);
    write_value(bytes,record2);
}

例子

int main() { 
    // Just a example, CHECK SIZE in real world.
    std::array<unsigned char,1024> buffer;

    serialize_stream(buffer.data());
    deserialize_stream(buffer.data());

}

如果这部分不受 time/storage 效率限制,请考虑使用序列化库来执行此操作。这些库可以将您的对象序列化为 XML 或 JSON 并轻松反序列化。您无需担心字节序或 POD 问题。