我正在尝试用С++实现一个霍夫曼压缩器。
简而言之,我有5个班级:
HuffmanTree
- 代表树形结构
TreeNode
- 代表树形结构
HuffmanArchiver
- 压缩/解压缩等。
BitStringWrite
- 写位。
BitStringRead
- 阅读比特。
我可以构建一个代码表并对二进制文件进行编码,但是我有一些关于读/写和解码阶段的问题。
当我进行编码阶段时,首先我将Huffman树保存在我的新文件中,如下所示:
void Archiver::encodeTree(BitStringWrite& bw, TreeNode* node){
if (node -> isLeaf()) {
bw.writeBit(1);
char symb = node->getChar();
bw.getStream().write(&symb, sizeof(symb));
}
else {
bw.writeBit(0);
encodeTree(bw, node->getLeftTree());
encodeTree(bw, node->getRightTree());
}
}
bw
这是类BitStringWrite
的一个实例,它实现如下:
BitStringWrite::BitStringWrite(std::ostream &_out_f) : _byte(0), _pos(0), _out_f(_out_f) {}
void BitStringWrite::writeBit(bool bit) {
if (_pos == 8)
flush();
if (bit == 1) {
_byte |= (1 << (7 - _pos));
}
_pos++;
}
void BitStringWrite::writeByte(char b){
for(int i = 0; i < 8; i++)
this -> writeBit((b >> i) & 1); //?????
}
void BitStringWrite::flush() {
if (_pos != 0) {
_out_f.write(&_byte, sizeof(char));
_pos = 0;
_byte = 0;
}
}
std::ostream& BitStringWrite::getStream(){
return _out_f;
}
我在writeByte
实施中不确定,但这里的主要问题是,如果我已经有writeByte
,我可能想要实现istream::write
函数吗?
例如
>cat test.in aaaabc
buildTable
函数将生成:a = 1
,b = 010
,c = 00
和= 011
(似乎最后一个符号只是\n
)。
xxd -b test.out
00000000: 01100011 01100010 00001010 01100001 00101111 11101000 cb.a/.
00000006: 01101100
注意,编码消息从第五个字节的最后一位开始。前五个(几乎)字节表示结构树。
好的,似乎编码阶段正在运行。现在让我们进入解码阶段。
解码阶段的主要功能是decompress
。它调用decodeTree
函数对Huffman树进行解码,然后根据该树生成代码表,然后对文本进行解码。
函数decodeTree
无法正常运行:
TreeNode* Archiver::decodeTree(BitStringRead& br, TreeNode* cur){
if (br.readBit()) {
return new TreeNode(br.readByte(), 0, false, NULL, NULL);
}
else {
TreeNode* left = decodeTree(br, cur-> getLeftTree());
TreeNode* right = decodeTree(br, cur-> getRightTree());
decodeTree(br, cur-> getRightTree());
return new TreeNode(0, 0, false, left, right);
}
}
我认为主要原因是因为它无法使用br
,类BitStringRead
的实例来正确读取树结构。
看看它是如何实现的:
BitStringRead::BitStringRead(std::istream &_in_f) : _pos(8), _in_f(_in_f) {}
bool BitStringRead::readBit() {
if (_pos == 8) {
_in_f.read(&_byte, sizeof(char));
_pos = 0;
}
return (_byte >> _pos++) & (char)1;
}
char BitStringRead::readByte() {
char sym = (char)0;
for (int i = 0; i < 8; i++){
sym |= ((1 & readBit()) << (i));
}
return sym;
}
假设我们在文件的开头,我有一个字节0001 0110
。我第一次调用readBit
函数。它读取第一个8
位。然后我再调用它3次,它没有读取任何内容,只是返回这些位的值。字符串中的第一个1
表示叶节点,我知道在叶节点之后有一个符号,所以我读了它。
由于readBit
实现,我认为它从第9位而不是第4位开始读取。