将二进制文件缓冲区的块读取为不同类型的



我正在尝试将二进制文件读入内存,然后像这样使用它:

struct myStruct {
    std::string mystring; // is 40 bytes long
    uint myint1; // is 4 bytes long
};
typedef unsigned char byte;
byte *filedata = ReadFile(filename); // reads file into memory, closes the file
myStruct aStruct;
aStruct.mystring = filedata.????

我需要一种方法来访问带有偏移量的二进制文件,并在该偏移量处获得一定的长度。如果我将二进制文件数据存储在 std::string 中,这很容易,但我认为使用它来存储二进制数据并不是做事的好方法。 (filedata.substr(offset, len))

相当广泛的(IMO)搜索没有发现任何相关的东西,有什么想法吗?如果您认为有必要,我愿意更改存储类型(例如更改为 std::vector)。

如果您不打算使用序列化库,那么我建议为每个类添加序列化支持:

struct My_Struct
{
    std::string my_string;
    unsigned int my_int;
    void Load_From_Buffer(unsigned char const *& p_buffer)
    {
        my_string = std::string(p_buffer);
        p_buffer += my_string.length() + 1; // +1 to account for the terminating nul character.
        my_int = *((unsigned int *) p_buffer);
        p_buffer += sizeof(my_int);
    }
};
unsigned char * const buffer = ReadFile(filename);
unsigned char * p_buffer = buffer;
My_Struct my_variable;
my_variable.Load_From_Buffer(p_buffer);

其他一些有用的接口方法:

unsigned int Size_On_Stream(void) const; // Returns the size the object would occupy in the stream.
void Store_To_Buffer(unsigned char *& p_buffer); // Stores object to buffer, increments pointer.

使用模板,您可以扩展序列化功能:

void Load_From_Buffer(std::string& s, unsigned char *& p_buffer)
{
    s = std::string((char *)p_buffer);
    p_buffer += s.length() + 1;
}
void template<classtype T> Load_From_Buffer(T& object, unsigned char *& p_buffer)
{
  object.Load_From_Buffer(p_buffer);
}

编辑1:不直接写结构的原因

在 C 和 C++ 中,结构的大小可能不等于其成员大小的总和。
允许编译器在成员之间插入填充或未使用的空格,以便成员在地址上对齐。

例如,32 位处理器喜欢在 4 字节边界上获取内容。 在结构中有一个char后跟一个int将使相对地址 1 上的int,而不是 4 的倍数。 编译器将填充结构,以便int在相对地址 4 上对齐。

结构可能包含指针或包含指针的项。
例如,std::string 类型的大小可能为 40,尽管字符串可能包含 3 个字符或 300 个字符。 它有一个指向实际数据的指针。

恩迪亚斯。
对于多字节整数,一些处理器,如最高有效字节(MSB),又名大端序,第一(人类读取数字的方式)或最低有效字节优先,又名小字节序。 小端序格式比大端序格式需要更少的电路来读取。

编辑 2:变体记录

输出数组和容器等内容时,必须决定是要输出整个容器(包括未使用的插槽)还是仅输出容器中的项目。 仅输出容器中的项将使用变体记录技术。

输出变体记录的两种技术:数量后跟项目或项目后跟哨兵。 后者是 C 样式字符串的编写方式,哨兵是 NUL 字符。

另一种技术是输出项目的数量,然后输出项目。 因此,如果我有 6 个数字,0、1、2、3、4、5,输出将是:
6//项目

0
1阿拉伯数字

3
四5

在上面的Load_From_Buffer方法中,我会创建一个临时的来保存数量,写出来,然后跟着容器中的每个项目。

您可以重载结构的 std::ostream 输出运算符和 std::istream 输入运算符,如下所示:

struct Record {
    std::string name;
    int value;
};
std::istream& operator>>(std::istream& in, Record& record) {
    char name[40] = { 0 };
    int32_t value(0);
    in.read(name, 40);
    in.read(reinterpret_cast<char*>(&value), 4);
    record.name.assign(name, 40);
    record.value = value;
    return in;
}
std::ostream& operator<<(std::ostream& out, const Record& record) {
    std::string name(record.name);
    name.resize(40, '');
    out.write(name.c_str(), 40);
    out.write(reinterpret_cast<const char*>(&record.value), 4);
    return out;
}
int main(int argc, char **argv) {
    const char* filename("records");
    Record r[] = {{"zero", 0 }, {"one", 1 }, {"two", 2}};
    int n(sizeof(r)/sizeof(r[0]));
    std::ofstream out(filename, std::ios::binary);
    for (int i = 0; i < n; ++i) {
        out << r[i];
    }
    out.close();
    std::ifstream in(filename, std::ios::binary);
    std::vector<Record> rIn;
    Record record;
    while (in >> record) {
        rIn.push_back(record);
    }
    for (std::vector<Record>::iterator i = rIn.begin(); i != rIn.end(); ++i){
        std::cout << "name: " << i->name << ", value: " << i->value
                  << std::endl;
    }
    return 0;
}

相关内容

  • 没有找到相关文章

最新更新