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

C++ C++;,读取二进制ifstream时的奇怪行为,c++,stl,binary,ifstream,file-read,C++,Stl,Binary,Ifstream,File Read,关于我的第一个问题, 我想谈谈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 下面

关于我的第一个问题, 我想谈谈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作为字符串结尾,但在此我以二进制读取。并使用数字格式

另一件奇怪的事情是,如果我用两次处理它,我就能使它工作,例如:

uint16_t version = 0;
char     buff;

 _stream.read(&buff, 1);
version = (buff << 8);
 _stream.read(&buff, 1);
version |= buff;
uint16\u t版本=0;
半焦;
_stream.read(&buff,1);

version=(buff版本字段由两个无符号字节(主版本、次版本)组成,而不是由一个无符号的短字节组成。您应该分别读取这两个版本号,以免在endianess问题中出现混乱


Endianess是特定于平台的。如果你坚持阅读一篇包含主次版本的短文,你可以解决它。但最终你会编写不那么清晰易懂的代码来解决你自己造成的问题。

这似乎是一个Endianess问题。那么这是什么呢?根据:

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

内存中布局的可视示例:

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

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

  • 逐字节读取
  • 读取值并使用VC++中的
    \u byteswap\u ushort
    或GCC中的
    \u builtin\u bswap16
    交换字节
  • 读取值并使用

  • 这里有一些谷歌食品供您参考:“little endian”和“big endian”。您首先需要精确地定义文件格式(可能使用EBNF表示法)作为旁白,如果您正在寻找独立于平台的代码,那么不能保证一个字节是8位(可能出现这种情况的平台也可能不支持固定宽度整数类型)如果您使用的是
    Qt
    ,我建议您使用
    QDataStream
    免费处理endian问题。@SamVarshavchik您是对的,我得出的结论是这是一个奇怪的bahoviour,但我忘记了我在学校上过的课,谢谢您的提示。不要这样做。2.这里绝对没有必要使用特定于供应商的扩展并使您的代码不可移植。@下划线\u d,添加了对自定义交换的引用implementations@DanielTrugman这让我想起了在学校上课的日子,我怎么会错过这个……5年前我会为那个错误打自己一巴掌。我想我在高级语言上花了太多时间。不管怎样,谢谢你的回答,我最后一个字节一个字节地读了te@HWalters有一些工具可以将给定endianess的ByTestStream中的数字转换到本地平台,例如
    ntohs()
    和类似的内容。因此,您可以得到一个包含独立于平台的主要版本和次要版本的简短版本。在本例中,与单独读取两个数字相比,这是不值得的。@ypnos,实际上您是对的,我已经一个字节一个字节地读取了,它更简单,更容易读取。但是它的编写方式n在规范中,我不明白它是两个分开的字节,我以为我是一个。