16進数形式の文字列について
ハッシュ値
を生成したりuniqid
などでユニークな文字列を生成した際、16進数形式
で文字列が生成されます。例えば、
- 画像アップロードされた場合に
1d5c1b66fefdec9ed1fb54af60dc27d957e60ca6.jpg
といったランダムな文字列を生成してファイル名が重複しないように制御 - メール認証のために
https://~~.jp/auth?key=16115b7e66a2068c1eb24f75d6ab826f8e658be4
といったURLを生成
などというときに使ったりすると思います。
もう少し文字列を圧縮しませんか?
このままでも問題ありませんが、16進数
からBASE64
に変換すれば、安全に使用可能な文字種の範囲内で文字数を圧縮できます。
文字数が短くなればDB等の容量削減や、URL等が短くなることによる通信量削減になります。
また、一部のメーラーでメール本文内のURLが折り返されて切れてしまう可能性も少しだけ減らせます。
16進数 | BASE64 |
---|---|
3バイト (16777216通り)の情報を6文字 ($16^6 = 16777216$)で表せる | 3バイトの情報を4文字 ($64^4=16777216$)で表せる |
したがって、16進数
と比べてBASE64
だと文字数を$\frac{2}{3}$に圧縮できます。
PHPでの具体的な手順
具体的には、16進数
→ バイナリ文字列
→ BASE64
という二段階で変換をかけていきます。
また、BASE64の文字列の中で+
, /
, =
はシステムとの相性が悪いケースが多いので、より安全な記号に変換します。
// BASE64に変換する
$str = '変換前16進数文字列';
$str = strtr(base64_encode(pack('H*', $str)), ['+' => '-', '/' => '_', '=' => '']);
// 16進数に戻す
$str = bin2hex(base64_decode(strtr($str, ['-' => '+', '_' => '/'])));
// ※PHP5.4未満の場合は [~~] を array(~~) に置き換えてください。
結果
c42ffbaacf25ba233cbf731d33001ed341eb6a31
→ xC_7qs8luiM8v3MdMwAe00HrajE
40文字から27文字に減りました。
注意点
変換対象の文字列が奇数文字数だと、16進数に戻したときに末尾に余分な0が追加されてしまいます。
文字数が奇数になる可能性がある、かつ、16進数に戻せるようにする必要がある場合は、強引ですが以下のようにして回避します。
// 奇数の場合は「~」の文字を足し、戻すときに~の文字がついていれば末尾の0を取り除く
// ※「~」の文字列が許容されている前提
// BASE64に変換する
$str = (strlen($str) % 2 ? '~' : '') . strtr(base64_encode(pack('H*', $str)), ['+' => '-', '/' => '_', '=' => '']);
// 16進数に変換する
$str = substr(bin2hex(base64_decode(strtr($str, ['-' => '+', '_' => '/', '~' => '']))), 0, strpos($str, '~') === 0 ? -1 : strlen($str));