インストール
- mpyw/BaseUTF8 … 符号化を扱うやつ
mpyw/FileTD … 上記を利用した応用スクリプト(後述)
何これ
例を見ればわかるのでREADME.md
からそのまま引用
require 'vendor/autoload.php';
use mpyw\BaseUTF8\Coder;
$coder = new Coder; // Base64
echo $coder->encode('foobar') . PHP_EOL; // Zm9vYmFy
echo $coder->decode('Zm9vYmFy') . PHP_EOL; // foobar
echo $coder->decode("Z m \n9v Y\tmFy") . PHP_EOL; // foobar
$coder = new Coder('ABCDabcd'); // Base8
echo $coder->encode('foobar') . PHP_EOL; // DBacdbbdDAacAbcC
echo $coder->decode('DBacdbbdDAacAbcC') . PHP_EOL; // foobar
$coder = new Coder('ンアッーイキソ!'); // UTF-8 Base8
echo $coder->encode('田所浩二') . PHP_EOL;
echo $coder->decode('!ア!アッッソン!アキンイソンン!アキーッソキア!アアーキッアイ') . PHP_EOL;
デフォルトの挙動はbase64_encode及びbase64_decodeとほとんど同じですが
-
=
のようなトレイラーは付加しない - コントロールシーケンス以外の無効なバイト列を検知したとき
RuntimeException
をスローする
という点だけ異なります。
何で作ったの
「マルチバイト文字で符号化したらかえってバイト長増えるじゃん?」
はい、確かにその通りなんですが、文字数は大きく抑えることが出来ます。先日TwitterがDMの最大文字数を10000文字まで拡張したので、これに便乗してDM経由のファイル転送クライアントを作ろうかなーと(厄介)。Twitterはバイト数ではなく文字数でカウントするのがミソです。
符号化効率の検証
Base64の場合とBase32768の場合を比較してみます。今回はQiitaのロゴを符号化してみます。
base64
$coder = new Coder;
$bytes = $coder->encode(file_get_contents('qiita.png'));
printf("Bytes: %d; Chars: %d\n", strlen($bytes), mb_strlen($bytes, 'UTF-8'));
/*
Bytes: 9980; Chars: 9980
*/
base32768
$headers = [
range("\x21", "\x7F"),
range("\xC2", "\xDF"),
range("\xE1", "\xEC"),
];
$trailer = range("\x80", "\xBF");
$table = $headers[0];
foreach ($headers[1] as $x) {
foreach ($trailer as $y) {
$table[] = "$x$y";
}
}
foreach ($headers[2] as $x) {
foreach ($trailer as $y) {
foreach ($trailer as $z) {
$table[] = "$x$y$z";
if (count($table) >= 32768) {
break 3;
}
}
}
}
$coder = new Coder($table);
$bytes = $coder->encode(file_get_contents('qiita.png'));
printf("Bytes: %d; Chars: %d\n", strlen($bytes), mb_strlen($bytes, 'UTF-8'));
/*
Bytes: 11628; Chars: 3992
*/
文字数という視点だと大幅に圧縮できていることが分かります。取り扱うデータによってはこれにgzdeflateおよびgzinflateを組み合わせると更に短縮出来るかもしれません。
2015/8/16 追記
そもそも任意のバイナリデータは画像に偽装出来、この手段を用いたほうが明らかに効率がいいことに気づいたのでFileTDはやめました