大多数通用压缩算法使用一字节粒度。
让我们考虑以下字符串:
"XXXXYYYYXXXXYYYY"
- Run-Length-Encoding 算法会说:“那是 4 'X',接着是 4 'Y',接着是 4 'X',接着是 4 'Y'”
- Lempel-Ziv 算法会说:“这是字符串 'XXXXYYYY',后跟相同的字符串:所以让我们将第二个字符串替换为对第一个字符串的引用。”
- 霍夫曼编码算法会说:“那个字符串中只有 2 个符号,所以每个符号我只能使用一位。”
现在让我们用 Base64 编码我们的字符串。这是我们得到的:
"WFhYWFlZWVlYWFhYWVlZWQ=="
现在所有的算法都在说:“那是什么乱七八糟的东西?” . 而且他们不太可能很好地压缩该字符串。
提醒一下,Base64 基本上通过将 (0...255) 中的 3 字节组重新编码为 (0...63) 中的 4 字节组来工作:
Input bytes : aaaaaaaa bbbbbbbb cccccccc
6-bit repacking: 00aaaaaa 00aabbbb 00bbbbcc 00cccccc
然后将每个输出字节转换为可打印的 ASCII 字符。按照惯例,这些字符是(这里每 10 个字符有一个标记):
0 1 2 3 4 5 6
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
例如,我们的示例字符串以十六进制等于 0x58 的一组三个字节开始(字符“X”的 ASCII 代码)。或者二进制:01011000。让我们应用 Base64 编码:
Input bytes : 0x58 0x58 0x58
As binary : 01011000 01011000 01011000
6-bit repacking : 00010110 00000101 00100001 00011000
As decimal : 22 5 33 24
Base64 characters: 'W' 'F' 'h' 'Y'
Output bytes : 0x57 0x46 0x68 0x59
基本上,在原始数据流中明显的“3 倍字节 0x58”模式在编码数据流中不再明显,因为我们已将字节分解为 6 位数据包并将它们映射到现在出现的新字节是随机的。
或者换句话说:我们破坏了大多数压缩算法所依赖的原始字节对齐。
无论使用何种压缩方法,通常都会对算法性能产生严重影响。这就是为什么你应该总是先压缩然后编码。
对于加密来说更是如此:首先压缩,然后加密。
编辑 - 关于 LZMA 的说明
正如 MSalters 所注意到的,xz 正在使用的 LZMA 正在处理比特流而不是字节流。
尽管如此,这个算法也会受到 Base64 编码的影响,这与我之前的描述基本一致:
Input bytes : 0x58 0x58 0x58
As binary : 01011000 01011000 01011000
(see above for the details of Base64 encoding)
Output bytes : 0x57 0x46 0x68 0x59
As binary : 01010111 01000110 01101000 01011001
即使在位级别上工作,在输入二进制序列中识别模式也比在输出二进制序列中识别模式要容易得多。