13
14

More than 5 years have passed since last update.

UTF-8のマルチバイト文字まで対応したBaseXXエンコーダ/デコーダを書いてみた

Last updated at Posted at 2015-08-14

インストール

  • 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のロゴを符号化してみます。

logo-square-ed574fbb8006572197b178cd7f9ad00c.png

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はやめました

13
14
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14