使用 ifstream 将二进制数据读入结构体

Reading binary data into struct with ifstream

提问人:Dan 提问时间:3/5/2014 最后编辑:Dan 更新时间:6/5/2023 访问量:37267

问:

我正在尝试使用 ifstream 从文件中读取二进制数据。

具体来说,我正在尝试用从文件中读取的数据填充这个“Header”结构:

struct Header {
    char id[16];
    int length;
    int count;
};
  1. 现在,如果我以这种方式读取文件,结果正是我想要的:

    input.read((char*)&hdr, sizeof(hdr));
    
  2. 但是,如果我手动读取结构的每个变量,结果是胡言乱语:

    input.read((char*)&hdr.id,     sizeof(hdr.id));
    input.read((char*)&hdr.length, sizeof(hdr.length));
    input.read((char*)&hdr.count,  sizeof(hdr.count));
    

我的问题是,这里发生了什么使这两种方法返回不同的结果?

C++ 结构 体二进制 ifstream

评论

1赞 Chris McGrath 3/5/2014
你读了,你是如何定义它们的?或者,它们本来就是和?lengthcounthdr.lengthhdr.count
1赞 WhozCraig 3/5/2014
如果您认为如何将数据写入该文件(或其他人这样做)有关,那么您是对的;确实如此。简短的回答:要求“至少”与其成员大小的总和一样大,并且在包括实现成员填充时可以更大。在编写整个结构时,不能保证成员会相互支撑,但您正在阅读它,期望它们是相互支撑的。也就是说,如果你正在“写”一个橙子,试图“读”一袋橘子不一定会起作用。sizeof(Header)
0赞 Dan 3/6/2014
@ChrisMcGrath 对不起,这是一个复制粘贴错误,现在修复了
2赞 WhozCraig 3/6/2014
@Dan 你“只知道它应该如何阅读”。是怎么回事?第一种方法假设编写此内容的人使用与您正在使用的相同布局和填充来编写此内容。如果没有填充问题,那么第二个将起作用(并且几乎是开始执行此操作的首选方法,无论是阅读还是写作)。考虑到该结构中数据成员的布局和大小,如果元素之间、最后一个元素之后或两者之间有填充,那会很奇怪,但我见过更奇怪的事情。 与每个成员相比会说很多。sizeof(hdr)sizeof()
1赞 WhozCraig 3/6/2014
@Dan 不要指望它总是这样。你很幸运,填充物和内脏都不是问题,因为它们通常是。

答:

9赞 Blaz Bratanic 3/5/2014 #1

正如上面的评论所述,您可能缺少 hdr.length 和 hdr.count。 我用 gcc 4.8 和 clang 3.5 尝试过,它工作正常。

#include <iostream>
#include <fstream>

#pragma pack(push, r1, 1)
struct Header {
    char id[15];
    int length;
    int count;
};
#pragma pack(pop, r1)

int main() {
  Header h = {"alalalala", 5, 10};

  std::fstream fh;
  fh.open("test.txt", std::fstream::out | std::fstream::binary);
  fh.write((char*)&h, sizeof(Header));
  fh.close();

  fh.open("test.txt", std::fstream::in | std::fstream::binary);

  fh.read((char*)&h.id, sizeof(h.id));
  fh.read((char*)&h.length, sizeof(h.length));
  fh.read((char*)&h.count, sizeof(h.count));

  fh.close();

  std::cout << h.id << " " << h.length << " " << h.count << std::endl;
}

评论

1赞 WhozCraig 3/5/2014
将字段更改为并再次运行,只是为了踢球。idchar id[15]
0赞 Blaz Bratanic 3/6/2014
你是对的。添加了编译指示以修复对齐方式。
0赞 Jamie 10/22/2021
通过处理字节顺序,这将大大改善。如果这是在大端系统上写的,而在小端系统上阅读,事情会很糟糕。
12赞 Marcel Zebrowski 8/26/2018 #2

也可以一步读取结构。

fh.read((char*)&h, sizeof(Header));

评论

3赞 Jean-Christophe 7/24/2020
使用此方法时,必须注意避免内存对齐填充。请参阅结构填充和填料
0赞 starriet 9/19/2023
@Jean-Christophe 谢谢,这似乎很重要。您能给我们举一个简单的例子来说明上述代码何时失败吗?相反,接受的答案中的代码似乎存在您提到的问题(结构填充):如果结构是填充的,则由于填充的内存,逐个元素读取一个元素可能会损坏,不是吗?(除非移动位置,包括填充的内存)fstream::read
0赞 Jean-Christophe 10/10/2023
@starriet 这是不可移植的。如果文件最初在不同的指令集上序列化,则上述代码将导致读取损坏。一个典型的情况是从 32 位计算机到 64 位计算机的消息传递。另一种失败情况是读取打包数据上的解压缩结构/标量。