2016-06-30 243 views
18

我最近压缩了一些文件,我注意到base64编码的数据似乎压缩得很糟糕。这里有一个例子:为什么base64编码的数据压缩如此糟糕?

  • 原始文件:429,7 MIB
  • 压缩通过xz -9
    13,2 MiB/429,7 MiB = 0,0314,9 MiB/s1:28
  • base64,并压缩通过xz -9
    26,7 MiB/580,4 MiB = 0,0462,6 MiB/s3:47
  • base64原始压缩的xz文件:
    17,8 MiB在几乎没有时间=预期1.33x尺寸增大

那么可以观察到的是:

  • XZ压缩真的好☺
  • base64编码数据不压缩它比未编码的压缩文件大2倍
  • base64-then-compress明显更差且比comp慢ress-then-base64

这是怎么回事? Base64是一种无损,可逆的算法,它为什么会对压缩产生如此大的影响? (我也尝试了gzip,结果类似)。

我知道它没有意义base64-then-compress一个文件,但大多数时候一个人不能控制输入文件,我会认为,由于实际的信息密度(或任何它被称为)的base64编码文件将几乎相同的非编码版本,因此是类似的可压缩。

+0

“_base64-then-compress_明显地比_compress-then-base64_更糟且更慢” - 更不用说二者适用于所有意图和目的无关。 – MSalters

回答

4

压缩必然是一个作用于多个位的操作。尝试压缩个人“0”和“1”没有任何收益。即便如此,压缩一次只能在有限的一些位上工作。 xz中的LZMA算法不会一次考虑全部36亿比特。它看起来更小的字符串(< 273字节)。

现在看看base-64编码的作用:它用一个4字节的字替换一个3字节(24位)的字,只使用256个可能值中的64个。这给你x1.33的增长。

现在很清楚,这种增长必须导致一些子字符串增长超过编码器的最大子字符串大小。这导致它们不再被压缩为单个子字符串,而是作为两个单独的子字符串的确被压缩。由于你有一个批次的压缩(97%),你显然有这样的情况,即很长的输入子串被压缩为一个整体。这意味着您将有许多子字符串扩展到base-64,超过编码器可以处理的最大长度。

38

大多数通用的压缩算法使用单字节粒度

让我们考虑以下字符串:

"XXXXYYYYXXXXYYYY" 
  • 游程编码算法会说:“那4 'X',其次是4 'Y',其次是4 'X' ,然后是4'Y'“
  • Lempel-Ziv算法会说:”这就是字符串'XXXXYYYY',后面是相同的字符串:所以让我们用第一个字符串替换第二个字符串。
  • 霍夫曼编码算法会说:“该字符串中只有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 
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/ 

举例来说,我们的例子中字符串等于十六进制将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 

即使是在比特级别的工作,它更容易识别的模式输入二进制序列比输出二进制序列。

+0

感谢您的详细解释。所以base64是“混淆”比特流中的重复。有趣的是,@MSalters有一个问题的一部分是我的数据是可压缩的,如果我从'/ dev/urandom'提供'xz'数据,它根本不压缩(如预期的那样),但'/ dev/urandom通过'base64'压缩到〜77%,非常接近霍夫曼编码的理论最大值75%。 –

+0

是的,任何实现霍夫曼编码的算法至少都会发现在Base64编码的字符串中只使用了256个符号中的64个(在这种情况下是字节)。对于随机数据,所有64个符号应该得到均匀分布,因此导致约75%的压缩比。 – Arnauld

+0

加1表示“所有算法现在都在说:”这是什么混乱?“。” – user305883

相关问题