RLE在C++中易导致膨胀,关键在格式设计与边界控制;用std::vector存“次数+字节”会因内存对齐、映射困难和缺转义机制而翻车;应直接写二进制流,长度1–255,0作字面量标记;需防解压炸弹,加计数上限与缓冲区检查。

RLE在C++里不是“实现出来就能压”,而是“不 careful 就会越压越大”——关键在格式设计和边界控制,不是循环写对了就行。
为什么用std::vector<:pair uint8_t>></:pair>做中间结构容易翻车
很多人一上来就用 std::vector<:pair uint8_t>></:pair> 存“次数+字节”,逻辑清晰但埋了三个坑:一是内存多分配一倍(每个 pair 至少 2 字节对齐),二是无法直接映射到二进制流(解压时还得再拆一次),三是没预留转义机制——遇到原始数据里恰好有“0xFF 0x01”这种组合,会被误判为“重复 255 次字节 1”。
实操建议:
- 压缩阶段直接写二进制流,每组用两个
uint8_t:先长度后值,不包装、不缓存 - 长度字段只取 1–255;0 留作特殊标记(比如表示“字面量段开始”),否则无法处理孤立字节
- 如果必须用容器暂存,改用
std::vector<uint8_t></uint8_t>平铺存储,偶数索引是长度、奇数索引是值
怎么避免压缩后文件变大
RLE膨胀是常态,不是 bug。当平均游程长度
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 加一个预检开关:
if (run_length ,把短游程当字面量直通 - 对输入做简单熵估算:统计相邻字节相等的频次,低于 30% 就跳过压缩,直接 memcpy
- 不要对已压缩文件(如
.jpg、.zip)再套 RLE,这类数据游程长度均值通常为 1.02
二进制文件读写必须用 std::ios::binary
用 std::ifstream 默认文本模式读 .bin,Windows 下会把 0x0A 自动转成 0x0D 0x0A,解压时长度错位,输出全乱;Linux 虽不转换,但换行符仍可能被截断或补零。
实操建议:
- 打开文件务必指定模式:
std::ifstream in("in.bin", std::ios::binary); - 读取用
in.read(reinterpret_cast<char>(&byte), sizeof(byte))</char>,别用>>或getline - 写入前清空缓冲区:
out.flush();,尤其在嵌入式小内存环境里,未刷出的最后几个字节容易丢失
解压时如何防“解压炸弹”
恶意构造的 RLE 流可能含“255 0x00”重复上千次,解压时瞬间申请几 MB 内存,导致程序崩溃或 OOM。标准库不做长度校验,全靠你自己拦。
实操建议:
- 解压循环内加硬上限:
if (count > 1024) { throw std::runtime_error("RLE count too large"); } - 每次写入前检查目标 buffer 剩余空间,用
std::vector<uint8_t>::reserve()</uint8_t>预分配但不初始化 - 对输入流做单次扫描预估最大可能输出尺寸:
max_out_size = input_size * 255,超限直接拒绝
真正难的不是写通 compress() 和 decompress() 两个函数,而是想清楚:你的数据到底有没有游程?长度分布什么样?要不要为 1% 的异常字节付出 2× 空间代价?这些判断点,比 for 循环多写三行代码重要得多。


















