大多数通用压缩算法都使用一字节粒度。
让我们来考虑下面的字符串:
"XXXXYYYYXXXXYYYY"
- 一个Run-Length-Encoding算法会说:"这是4个'X',后面是4个'Y',再接着是4个'X',最后是4个'Y'"
- 一个Lempel-Ziv算法会说:"这是字符串 'XXXXYYYY',后面跟着相同的字符串:所以我们用对第一个字符串的引用来代替第二个字符串。"
- 一个Huffman编码算法会说:"那个字符串中只有2个符号,因此我可以每个符号使用一个比特位。"
现在让我们使用Base64编码我们的字符串。这是我们得到的结果:
"WFhYWFlZWVlYWFhYWVlZWQ=="
现在所有的算法都在说:“这是什么混乱?”。他们不可能将那个字符串压缩得很好。
提醒一下,Base64基本上通过将3字节组(0...255)重新编码为4字节组(0...63)来工作:
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所注意到的,LZMA(即xz正在使用的算法)是针对位流而非字节流进行操作的。
然而,这个算法也会受到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
即使在位级别上工作,识别输入二进制序列中的模式比识别输出二进制序列中的模式要容易得多。
即使在位级别上工作,识别输入二进制序列中的模式比识别输出二进制序列中的模式要容易得多。