C++,读取二进制 ifstream 时的奇怪行为

Posted

技术标签:

【中文标题】C++,读取二进制 ifstream 时的奇怪行为【英文标题】:C++, weird behavior while reading binary ifstream 【发布时间】:2018-03-14 13:28:41 【问题描述】:

对于我在这里的第一个问题, 我想谈谈用 C++ 读取二进制文件; 我正在重新编码一个 ID3 标签库。

我正在解析二进制文件的标头, 前10字节如下:

ID3    = 3 bytes = constant identifier
0xXXXX = 2 bytes = version (MSB: major version, LSB: minor. eg: 0x0301 = v3.1)
0xXX   = 1 byte  = some flags
4*0xXX = 4 bytes = size

这是处理该代码的一段代码:

char          id[4];
uint16_t      version;
uint8_t       flags;
uint32_t      size;
std::ifstream _stream;

_stream = std::ifstream(_filename, std::fstream::binary);

_stream.read(id, 3);
id[3] = 0;
// process id
_stream.read((char *)&version, 2);
// process version
_stream.read((char *)&flags, 1);
// process flags
_stream.read((char* )&size, 4);
// process flags
_stream.close();

除了版本,一切都很好。 假设它是 v3.0 (0x0300), 版本中设置的值是 0x03,我会在文本模式下理解这种行为,因为它会将 0x00 视为字符串的结尾,但这里我以二进制形式读取。并使用数字格式。

其他奇怪的事情,如果我处理2次我可以让它工作,例如:

uint16_t version = 0;
char     buff;

 _stream.read(&buff, 1);
version = (buff << 8);
 _stream.read(&buff, 1);
version |= buff;

在这种情况下版本的值为0x0300。

你知道为什么第一种方法不能正常工作吗? 我是不是做错了什么?

不管怎样,谢谢你的帮助,

干杯!

【问题讨论】:

这里有一些谷歌食物给你:“小端​​”和“大端”。 您首先需要精确定义您的文件格式(可能是 EBNF 表示法) 顺便说一句,如果您正在寻找与平台无关的代码,则不能保证一个字节是 8 位(可能出现这种情况的相同平台也可能不支持固定宽度整数类型) 如果你使用Qt,我推荐使用QDataStream,它可以免费处理字节序问题。 @SamVarshavchik 你说得对,我马上得出结论,这是一种奇怪的行为,但我忘记了我在学校上过的课,谢谢你的提示。 【参考方案1】:

版本字段不是由一个无符号短字节组成,而是由两个无符号字节组成(主要版本,次要版本)。您应该分别阅读这两个版本号,以免被字节序问题弄得乱七八糟。

Endianess 是特定于平台的。如果您坚持阅读结合主要和次要版本的单个短片,您可以解决它。但最终,您编写的代码不够简洁和易于理解来解决您自己创建的问题。

【讨论】:

@HWalters 有一些工具可以将给定字节流中的数字转换为本地平台,例如ntohs() 等。因此,您可以获得一个包含主要版本和次要版本的短片,它与平台无关。与简单地单独读取两个数字相比,在此示例中不值得。 @ypnos,实际上你是对的,我最终逐字节阅读,它更简单,更容易阅读。但它在规范中的写法,我不明白它是两个分开的字节,我以为我是一个。【参考方案2】:

这似乎是一个字节序问题。那是什么?根据Wikipedia:

字节序是指字节排列成较大数值的顺序,当存储在计算机内存或辅助存储器中时

内存中布局的可视化示例:

Image origin

当您一次性读取该值时,字节会重新排列,这可能是因为它们的写入方式和读取方式不一致。

由于您知道它们在内存中的排列顺序,因此您应该执行以下操作之一:

    逐字节读取。 在 VC++ 中使用 _byteswap_ushort 或在 GCC 中使用 __builtin_bswap16 读取值并交换字节 使用custom implementation 读取值并交换字节

【讨论】:

不要做 #2。绝对没有必要在这里使用特定于供应商的扩展并使您的代码不可移植。 @underscore_d,添加了对自定义交换实现的引用 @DanielTrugman 这让我想起了当年在学校的课,我怎么会错过这个……我会为 5 年前的那个错误扇自己耳光。我想我在高级语言上花了太多时间。无论如何,感谢您的回答,我最终逐字节阅读了它

以上是关于C++,读取二进制 ifstream 时的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

为啥 c++ ifstream 不能从设备读取?

C++中,ifstream和ofstream定义文件流的区别

c++中的ifstream位置

C++问题:ifstream流一直找不到文件结束符 while循环一直没有跳出

C++之文件的读取和写入操作(文本文件和二进制文件)

从 cython c 调用 python 函数时的奇怪行为