最近、いくつかのファイルを圧縮していたのですが、base64 でエンコードされたデータの圧縮率が非常に悪いことに気づきました。次に例を示します。
- 元のファイル:429.7MB
- 圧縮方法
xz -9
:
13,2 MiB / 429,7 MiB = 0,031
4,9 MiB/s
1:28
base64
それを次のように圧縮しますxz -9
:
26,7 MiB / 580,4 MiB = 0,046
2,6 MiB/s
3:47
base64
元の圧縮されたxzファイル:
17,8 MiB
ほとんど時間をかけずに = 予想される1.33x
サイズの増加
したがって、次のことがわかります。
- xz は本当によく圧縮されます ☺
- base64でエンコードされたデータは圧縮率が低く、エンコードされていない圧縮ファイルの2倍の大きさになります。
- base64 で圧縮大幅に劣悪で遅い圧縮してからbase64
なぜそうなるのでしょうか? Base64 はロスレスで可逆的なアルゴリズムですが、なぜ圧縮にこれほど影響を与えるのでしょうか? (gzip でも試しましたが、同様の結果になりました)。
意味がないのは分かっていますbase64 で圧縮ファイルですが、ほとんどの場合、入力ファイルを制御することはできないため、base64 でエンコードされたファイルの実際の情報密度 (または何と呼ぶか) はエンコードされていないバージョンとほぼ同じであるため、同様に圧縮可能であると考えられます。
ベストアンサー1
一般的な圧縮アルゴリズムのほとんどは、1バイトの粒度。
次の文字列を考えてみましょう。
"XXXXYYYYXXXXYYYY"
- ランレングスエンコーディングアルゴリズムでは次のようになります。「これは 4 つの「X」、その後に 4 つの「Y」、その後に 4 つの「X」、その後に 4 つの「Y」です」
- Lempel-Ziv アルゴリズムでは次のようになります。「これは文字列「XXXXYYYY」であり、その後に同じ文字列が続きます。したがって、2 番目の文字列を 1 番目の文字列への参照に置き換えましょう。」
- ハフマン符号化アルゴリズムは次のようになります。「その文字列には 2 つのシンボルしかないので、シンボルごとに 1 ビットだけ使用できます。」
それでは、文字列を 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+/
たとえば、この例の文字列は、16 進数で 0x58 (文字「X」の ASCII コード) に相当する 3 バイトのグループで始まります。または、2 進数では 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
基本的に、元のデータ ストリームでは明らかだったパターン「バイト 0x58 の 3 倍」は、エンコードされたデータ ストリームではもう明らかではありません。これは、バイトを 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
ビット レベルで作業する場合でも、出力バイナリ シーケンスよりも入力バイナリ シーケンスのパターンを認識する方がはるかに簡単です。