C++,读取二进制 ifstream 时的奇怪行为
C++, weird behavior while reading binary ifstream
对于我的第一个问题,
我想谈谈在 C++ 中读取二进制文件;
我正在重新编码 ID3 标签库。
我正在解析 header 这是一个二进制文件,
前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;
在这种情况下,version 的值为 0x0300。
你知道为什么第一种方法不能正常工作吗?
我做错了什么吗?
无论如何,谢谢你的帮助,
干杯!
版本字段不是一个无符号短整型而是两个无符号字节(主要版本,次要版本)。您应该分别阅读这两个版本号,以免在字节顺序问题中受到破坏。
Endianess 是特定于平台的。如果您坚持阅读结合了主要版本和次要版本的单个短片,则可以解决它。但最终您编写的代码不够清晰易懂,无法解决您自己创建的问题。
这似乎是字节顺序问题。那是什么?根据 Wikipedia:
Endianness refers to the sequential order in which bytes are arranged into larger numerical values, when stored in computer memory or secondary storage
内存中布局的可视化示例:
当您一次性读取值时,字节会重新排列,这可能是因为它们的写入方式和读取方式不一致。
既然您知道它们在内存中的顺序,您应该执行以下操作之一:
- 逐字节读取。
- 读取值并使用 VC++ 中的
_byteswap_ushort
或 GCC __builtin_bswap16
中的 __builtin_bswap16
交换字节
- 读取值并使用 custom implementation
交换字节
对于我的第一个问题, 我想谈谈在 C++ 中读取二进制文件; 我正在重新编码 ID3 标签库。
我正在解析 header 这是一个二进制文件, 前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;
在这种情况下,version 的值为 0x0300。
你知道为什么第一种方法不能正常工作吗? 我做错了什么吗?
无论如何,谢谢你的帮助,
干杯!
版本字段不是一个无符号短整型而是两个无符号字节(主要版本,次要版本)。您应该分别阅读这两个版本号,以免在字节顺序问题中受到破坏。
Endianess 是特定于平台的。如果您坚持阅读结合了主要版本和次要版本的单个短片,则可以解决它。但最终您编写的代码不够清晰易懂,无法解决您自己创建的问题。
这似乎是字节顺序问题。那是什么?根据 Wikipedia:
Endianness refers to the sequential order in which bytes are arranged into larger numerical values, when stored in computer memory or secondary storage
内存中布局的可视化示例:
当您一次性读取值时,字节会重新排列,这可能是因为它们的写入方式和读取方式不一致。
既然您知道它们在内存中的顺序,您应该执行以下操作之一:
- 逐字节读取。
- 读取值并使用 VC++ 中的
_byteswap_ushort
或 GCC__builtin_bswap16
中的__builtin_bswap16
交换字节 - 读取值并使用 custom implementation 交换字节