C++读取二进制文件出现乱码:求原因分析与解决方案
二进制文件读取乱码问题分析与解决
问题背景
生成二进制文件的代码
#include <fstream> int main() { unsigned char buf[] = { 0x06, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x79, 0x31, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x0f, 0x00, 0x00, 0xc8, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f, 0x79, 0x32, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x10, 0x00, 0x00, 0xa0, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f, 0x79, 0x33, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x11, 0x00, 0x00, 0x70, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f, 0x79, 0x34, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x12, 0x00, 0x00, 0x20, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x67, 0x69, 0x72, 0x6c, 0x31, 0x00, 0x00, 0x00, 0x66, 0x13, 0x00, 0x00, 0x48, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x67, 0x69, 0x72, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x66, 0x13, 0x00, 0x00, 0x48, 0x42, 0x9a, 0x99, 0xd9, 0x3f}; std::ofstream fout("input", std::ofstream::binary); fout.write((char *)buf, sizeof(buf)); fout.close(); return 0; }
文件结构
首字节0x06表示记录总数,后续每条记录包含:
name:8字节gender:1字节('m'或'f')age:4字节weight:4字节(float)height:4字节(float)
定义的读取结构体
struct Person{ unsigned char name[8]; unsigned char gender; unsigned int age; float weight,height; }person[100];
读取代码
using namespace std; ifstream fin; fin.open("input",ios::binary); int n; fin.read(reinterpret_cast<char*>(&n),sizeof(n)); for(int i=0;i<n;i++) fin.read(reinterpret_cast<char*>(&person[i]),sizeof(person[i]));
输出乱码情况
输出代码:
for(int i=0;i<n;i++) cout<<person[i].name<<' '<<person[i].gender<<' '<<person[i].age<<' '<<person[i].weight<<' '<<person[i].height<<endl;
输出结果:
boy1 2577023688 7.00208e+28 1.81062e-41 � 863596386 0 6.25119e-42 pB���?boy4 309133312 40 1.7 girl1 2577023560 1.74727e+25 4.63068e-39 � 0 0 0 0 0 0
问题原因
1. 记录数读取方式错误
文件首字节是单字节的记录数(0x06),但代码中用int n读取了4字节,会把后续3个0x00也读入n。虽然小端系统下n的值碰巧是6,但这一步已经消耗了4字节(正确应该只消耗1字节),导致后续所有记录的读取位置直接偏移了3字节,数据完全错位。
2. 结构体内存对齐导致字节偏移
C++编译器会自动对结构体成员进行内存对齐以提升访问效率:
name[8]占8字节(已满足对齐要求)gender占1字节,为了让后面的age(4字节)对齐到4字节边界,编译器会在gender后填充3个空字节
最终sizeof(Person)为24字节,但文件中每条记录实际只有21字节(8+1+4+4+4),每次读取24字节会跨记录读取,进一步加剧数据混乱。
解决方案
1. 修正记录数读取逻辑
改用单字节类型读取记录数,避免多读取字节:
unsigned char count; fin.read(reinterpret_cast<char*>(&count), sizeof(count)); int n = static_cast<int>(count);
2. 取消结构体内存对齐
通过编译器指令强制结构体按1字节对齐,消除填充字节:
// 兼容MSVC、GCC、Clang的写法 #pragma pack(push, 1) struct Person{ unsigned char name[8]; unsigned char gender; unsigned int age; float weight,height; } __attribute__((packed)) person[100]; #pragma pack(pop)
3. 手动逐个读取成员(更安全)
如果不想依赖编译器指令,可逐个读取结构体成员,完全规避对齐问题:
for(int i=0;i<n;i++){ fin.read(reinterpret_cast<char*>(person[i].name), sizeof(person[i].name)); fin.read(reinterpret_cast<char*>(&person[i].gender), sizeof(person[i].gender)); fin.read(reinterpret_cast<char*>(&person[i].age), sizeof(person[i].age)); fin.read(reinterpret_cast<char*>(&person[i].weight), sizeof(person[i].weight)); fin.read(reinterpret_cast<char*>(&person[i].height), sizeof(person[i].height)); }
4. 验证结果
修正后,输出会显示正确的内容,例如:
boy1 m 15 70.5 1.7 boy2 m 16 68 1.7 boy3 m 17 65 1.7 boy4 m 18 60 1.7 girl1 f 19 62 1.74 girl2 f 19 62 1.74
内容的提问来源于stack exchange,提问作者Rodri
相关产品推荐
相关产品推荐

