背景
大二上期末专周选了一道手撸哈夫曼树的题目,其中有一个非必实现功能点:将哈夫曼编码结果存储起来,方便下次直接读取使用。
当时设计的哈夫曼树结构体是这样的
// 字符-权重-编码
typedef struct Code
{
// 代表的字符
char character;
// 权重
int weight;
// 编码
string decode;
// 判断是否被匹配
bool isMatched;
} Code;
看下文件读写部分的代码
void writeToFile(Code arr[], int size) {
ofstream codeBook;
codeBook.open("hfmTree.dat", ios::out | ios::binary);
for (int i = 0; i < size; i++) {
codeBook.write(reinterpret_cast<const char *>(arr + i), sizeof(Code));
}
codeBook.close();
ofstream hfmTreeSize;
hfmTreeSize.open("hfmTreeSize.dat");
hfmTreeSize << size;
hfmTreeSize.close();
}
void readFromFile(Code arr[], int size) {
ifstream codeBook;
codeBook.open("hfmTree.dat", ios::in | ios::binary);
for (int i = 0; i < size; i++) {
codeBook.read(reinterpret_cast<char *>(arr + i), sizeof(Code));
}
codeBook.close();
}
写之前
读之后
可以发现,数据乱了......
原因
在C++中,std::string
实际上是一个动态分配内存的字符数组。
程序会根据'\0'
判断一个完整的字符串,看下以下这段程序
char arr[] = {'A', 'B', 0x00, 'C'};
string str(arr);
string str1(arr, sizeof(arr));
cout << str << endl;
cout << str1 << endl;
运行结果
所以大概可以猜测,就是std::string
导致的读写前后数据错乱。
解决方案
手动设置每个字段该写入多少个字节,并且同样通过指定的长度读取
// 二进制文件格式:
// 1B character , 4B weight , 1B isMatched ,
// 4B decodeLength , ($decodeLength)B decode
void writeToFile(Code arr[], int size) {
ofstream codeBook;
codeBook.open("hfmTree.dat", ios::out | ios::binary);
for (int i = 0; i < size; i++) {
Code code = arr[i];
// 写入 character
codeBook.write(&code.character, sizeof(char));
// 写入 weight
codeBook.write(reinterpret_cast<char *>(&code.weight), sizeof(int));
// 写入 isMatched
codeBook.write(reinterpret_cast<char *>(&code.isMatched), sizeof(bool));
// 写入decode
// 获取decode长度
int decodeLength = code.decode.length();
// 写入长度
codeBook.write(reinterpret_cast<char *>(&decodeLength), sizeof(int));
// 写入decode
codeBook.write(code.decode.c_str(), decodeLength);
}
codeBook.close();
ofstream hfmTreeSize;
hfmTreeSize.open("hfmTreeSize.dat");
hfmTreeSize << size;
hfmTreeSize.close();
}
void readFromFile(Code arr[], int size) {
ifstream codeBook;
codeBook.open("hfmTree.dat", ios::in | ios::binary);
for (int i = 0; i < size; i++) {
Code *code = arr + i;
// 读取 character
codeBook.read(&code->character, sizeof(char));
// 读取 weight
codeBook.read(reinterpret_cast<char *>(&code->weight), sizeof(int));
// 获取 isMatched
codeBook.read(reinterpret_cast<char *>(&code->isMatched), sizeof(bool));
// 获取长度
int decodeLength = 0;
codeBook.read(reinterpret_cast<char *>(&decodeLength), sizeof(int));
// 重新分配decode大小
code->decode.resize(decodeLength);
// 写入decode
codeBook.read(&code->decode[0], decodeLength);
}
codeBook.close();
}
读取结果