如何在Rust中将已生成的霍夫曼编码写入文件
解决方案
1. 用Serde + Bincode实现紧凑序列化
Bincode是轻量二进制序列化格式,体积紧凑、序列化/反序列化速度快,完全适配你的需求,且能直接和serde配合使用。
步骤:
在
Cargo.toml中添加依赖:[dependencies] bincode = "1.3" serde = { version = "1.0", features = ["derive"] } bitvec = { version = "1.0", features = ["serde"] }修改代码完成序列化与反序列化:
use bitvec::prelude::*; use std::{collections::HashMap, fs::File, io::{Read, Write}, path::Path}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] struct HuffmanData { dictionary: HashMap<String, char>, message: BitVec, } // 省略huffman函数定义(假设功能正常) fn huffman(file: File) -> HuffmanData { // 你的实现逻辑 unimplemented!() } fn main() { let path = Path::new("data").join("romeo-juliet.txt"); let file = File::open(path).expect("Could not open file"); let huffman_data: HuffmanData = huffman(file); // 保存压缩数据 let save_path = Path::new("./compressed-data/").join("romeo-juliet.huff"); let mut save_file = File::create(save_path).unwrap(); let serialized = bincode::serialize(&huffman_data).unwrap(); save_file.write_all(&serialized).unwrap(); // 解压示例:读取并反序列化 let mut read_file = File::open(save_path).unwrap(); let mut buffer = Vec::new(); read_file.read_to_end(&mut buffer).unwrap(); let deserialized: HuffmanData = bincode::deserialize(&buffer).unwrap(); }注意:建议给压缩文件使用
.huff这类自定义后缀,避免与原文本文件混淆;bitvec已内置serde支持,无需额外处理比特序列的序列化。
2. 手动写入字节(自定义文件格式)
若追求极致紧凑的体积,可自定义文件结构,手动写入字典与压缩数据,完全控制存储细节。
自定义文件结构(按写入顺序):
- 字典条目数量:用
u32存储(4字节) - 每个字典条目:
- 字符的UTF-8字节长度(
u8,1字节) - 字符的UTF-8字节数据
- 编码的比特长度(
u16,2字节) - 编码的字节数据
- 字符的UTF-8字节长度(
- 压缩消息的总比特数(
u64,8字节) - 压缩消息的字节数据
代码示例(写入部分):
use bitvec::prelude::*; use std::{collections::HashMap, fs::File, io::Write}; fn save_huffman_manual(dictionary: &HashMap<String, char>, message: &BitVec, file: &mut File) { // 写入字典条目数 let dict_count = dictionary.len() as u32; file.write_all(&dict_count.to_le_bytes()).unwrap(); for (code, char) in dictionary { // 写入字符的UTF-8数据 let char_bytes = char.to_string().as_bytes(); file.write_all(&[char_bytes.len() as u8]).unwrap(); file.write_all(char_bytes).unwrap(); // 写入编码的比特长度与字节数据 let code_bitvec = BitVec::<Msb0, u8>::from_iter(code.chars().map(|c| c == '1')); file.write_all(&(code_bitvec.len() as u16).to_le_bytes()).unwrap(); file.write_all(code_bitvec.as_raw_slice()).unwrap(); } // 写入压缩消息的总比特数与字节数据 file.write_all(&(message.len() as u64).to_le_bytes()).unwrap(); file.write_all(message.as_raw_slice()).unwrap(); }
关键优化点
- 字典结构:如果你的编码生成逻辑是从字符到编码(
HashMap<char, String>),建议序列化这个结构,解压时再转换为反向映射,这样字典的存储更高效,避免重复存储编码字符串。 - 比特序列存储:利用bitvec的
as_raw_slice()直接获取底层字节数组,配合总比特数即可准确还原原始比特序列,避免不必要的比特填充浪费。
内容的提问来源于stack exchange,提问作者Doruk
相关产品推荐
相关产品推荐

