web-dev-qa-db-ja.com

なぜbase64でエンコードされたデータはそれほど圧縮されないのですか?

最近、いくつかのファイルを圧縮していましたが、base64でエンコードされたデータは本当にひどく圧縮されているようです。次に例を示します。

  • 元のファイル:429,7 MiB
  • xz -9経由で圧縮:
    13,2 MiB / 429,7 MiB = 0,0314,9 MiB/s1:28
  • base64 itおよびxz -9を介して圧縮:
    26,7 MiB / 580,4 MiB = 0,0462,6 MiB/s3:47
  • base64元の圧縮xzファイル:
    17,8 MiBほとんど時間がない=予想される1.33xサイズの増加

観察できることは次のとおりです。

  • xzは本当に良いcompressを圧縮します
  • base64でエンコードされたデータは圧縮率が低く、エンコードされていない圧縮ファイルよりも2倍大きい
  • base64-then-compressは、compress-then-base64

どうすればいいの? Base64は可逆で可逆的なアルゴリズムですが、なぜ圧縮にそれほど影響するのですか? (私もgzipで試しましたが、同様の結果が得られました)。

私はそれがbase64-then-compressファイルには意味がないことを知っていますが、ほとんどの場合、入力ファイルを制御することはできません、そして、base64でエンコードされたファイルの実際の情報密度(またはそれが何であれ)は、エンコードされていないバージョンとほぼ同じであり、同様に圧縮可能であると考えていたでしょう。

42
Stefan Seidel

ほとんどの一般的な圧縮アルゴリズムは、1バイトの粒度で機能します。

次の文字列を考えてみましょう。

"XXXXYYYYXXXXYYYY"
  • Run-Length-Encodingアルゴリズムは次のように言います: "that's 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バイトのグループで始まります。またはバイナリ: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

基本的に、元のデータストリームで明らかだったパターン「3x byte 0x58」は、バイトを6ビットパケットに分割し、現在のように見える新しいバイトにマッピングしたため、エンコードされたデータストリームではもはや明白ではありませんランダムに。

つまり、ほとんどの圧縮アルゴリズムが依存している元のバイトアライメントが壊れています。

どのような圧縮方法を使用しても、通常はアルゴリズムのパフォーマンスに深刻な影響を及ぼします。そのため、常に最初に圧縮し、2番目にエンコードする必要があります。

これは暗号化の場合にさらに当てはまります。まず圧縮し、次に暗号化します。

編集-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

ビットレベルで作業しても、出力バイナリシーケンスよりも入力バイナリシーケンスのパターンを認識する方がはるかに簡単です。

89
Arnauld

圧縮は、必然的に複数のビットに作用する操作です。個々の「0」と「1」を圧縮しようとすることで得られる利益はありません。それでも、圧縮は通常、一度に限られたビットのセットで機能します。 xzのLZMAアルゴリズムは、36億ビットのすべてを一度に考慮しません。はるかに小さい文字列(<273バイト)を調べます。

次に、base-64エンコードの機能を見てみましょう。256バイトのうち64バイトのみを使用して、3バイト(24ビット)ワードを4バイトワードに置き換えます。これにより、x1.33の成長が得られます。

この成長により、一部のサブストリングがエンコーダーの最大サブストリングサイズを超えて成長する必要があることは明らかです。これにより、それらは単一のサブストリングとしてではなく、実際には2つの別々のサブストリングとして圧縮されます。

lotの圧縮(97%)があるため、非常に長い入力部分文字列が全体として圧縮される状況が明らかにあります。これは、エンコーダーが処理できる最大長を超えてbase-64展開された多くのサブストリングも持つことを意味します。

8
MSalters