base_convertで文字列圧縮
こんにちは、アシアルの岡本です。
とある事情により16進数の文字列を圧縮する必要に迫られました。
URLに載せる必要があるのでバイナリは駄目ですzlib関数は使えません。
「PHPのbase_convert関数を使って36進数に変えれば楽勝!」
と思ったのが運のつき。
長く険しい文字列圧縮の旅(大袈裟!)が始まってしまいました。
base_convert関数は数値の基数を任意に変換するPHPの標準関数です。
こいつを使えば10進数の数字を16進数に変換したり、
16進数の数字を2進数に変換するといったことが一発で行えます。
この関数を活用し「16777216」という10進数を16進数に圧縮した場合、
「ffffff」と8文字から6文字に25%も圧縮できるのです。
しかし、PHPマニュアルの警告には
「大きな数値で base_convert() を使用すると、
精度が失われてしまうことがあります。」
とありました。
試してみましたが、残念ならが事実でした。
80バイトもあるような長い文字列で利用すると、似てるけど少し違う数字になってしまいます。
残念です。
「さて、どうしよう・・・」
自力で進数の変換コードを書く気力のなかった、
文系プログラマの僕はbase_convertに拘って、
文字列を適当な長さ(13桁までいけました)で区切る選択肢を選びました。
そうして作り上げた圧縮クラスが以下のソースコードになります。
分割は、思ったより簡単ではありませんでした。
分割した左辺に0があった場合は圧縮→解凍したときに0が取れてしまうので、
パディングして直す必要があったり色々大変でした。
このクラスは、完成というには程遠いですが
(元の進数と圧縮に使う進数をsetするメソッドすらない…)
ニーズがあるか知りたいので公開してしまいます。
自由に使ってもらって構いませんが、無保証です。
うん、後で弊社の小川に説教されるかも…
手が空いたら、今度は自分でconvert処理を書くかもしれません。
base_convertでは36進数が限界で62進数が使えないので。
「クラス」といえば
アシアルのスクールにオブジェクト編が追加されました。
PHPオブジェクト指向編 効率的にプログラミングする手法
講師の小川の方が丁寧に正しいオブジェクトの書き方を教えますので、
PHPでオブジェクト使ってみたい方は是非一度ご検討ください。
とある事情により16進数の文字列を圧縮する必要に迫られました。
URLに載せる必要があるのでバイナリは駄目ですzlib関数は使えません。
「PHPのbase_convert関数を使って36進数に変えれば楽勝!」
と思ったのが運のつき。
長く険しい文字列圧縮の旅(大袈裟!)が始まってしまいました。
base_convert関数は数値の基数を任意に変換するPHPの標準関数です。
こいつを使えば10進数の数字を16進数に変換したり、
16進数の数字を2進数に変換するといったことが一発で行えます。
この関数を活用し「16777216」という10進数を16進数に圧縮した場合、
「ffffff」と8文字から6文字に25%も圧縮できるのです。
しかし、PHPマニュアルの警告には
「大きな数値で base_convert() を使用すると、
精度が失われてしまうことがあります。」
とありました。
試してみましたが、残念ならが事実でした。
80バイトもあるような長い文字列で利用すると、似てるけど少し違う数字になってしまいます。
残念です。
「さて、どうしよう・・・」
自力で進数の変換コードを書く気力のなかった、
文系プログラマの僕はbase_convertに拘って、
文字列を適当な長さ(13桁までいけました)で区切る選択肢を選びました。
そうして作り上げた圧縮クラスが以下のソースコードになります。
- /**
* compress_by_base_convert * N進数の文字列をもっと大きなM進数に置き換えて文字を圧縮するクラス * * @copyright Asial * @author Yuki Okamoto <yuki@asial.co.jp> */ - class
compressByBaseConvert - {
var $compress_base = 36; var $uncompress_base = 16; var $split = 13; var $delimiter = "-"; /** * compress * 圧縮します * * @param string $string * @return string */ public function compress ($string) { // 分割しやすいよう0でパディングして、桁を分割したい数の倍数にする。 $length = strlen($string); $padding_length = $this->split - ($length % $this->split); $join_length = ($length + $padding_length); $string = str_pad($string, $join_length, "0", STR_PAD_LEFT); // 分割し、0パディングを解除する $string_array = str_split($string, $this->split); $string_array[0] = ltrim($string_array[0], "0"); // base_convertで圧縮 foreach ($string_array as $split) { $compress = base_convert($split, $this->uncompress_base, $this->compress_base); $compress_array[] = $compress; } // 連結して返す return implode($this->delimiter, $compress_array); } /** * uncompress * 解凍します * * @param string $string * @return string */ public function uncompress($string) { // 配列に戻す $string_array = explode($this->delimiter, $string); // base_convertで解凍 foreach ($string_array as $split) { $uncompress = base_convert($split, $this->compress_base, $this->uncompress_base); $uncompress_array[] = $uncompress; } // 途中の0が落ちないように0パディングして連結 $uncompress_join = ""; foreach ($uncompress_array as $uncompress) { $uncompress_join .= str_pad($uncompress, $this->split, "0", STR_PAD_LEFT); } // 上の桁の0は不要なのでtrimして削除 $uncompress_join = ltrim($uncompress_join, 0); return $uncompress_join; } - }
- //
実証コード - $compress
= new compressByBaseConvert(); - $source
= "153733d3cd194c6b60e50f1dcf5cdf703a3a363031302d30352d31382036303a33313a3aa9e9a25ea53fb8a2"; - $archive
= $compress->compress($source); - $return
= $compress->uncompress($archive); - var_dump($source,
$archive, $return);
分割は、思ったより簡単ではありませんでした。
分割した左辺に0があった場合は圧縮→解凍したときに0が取れてしまうので、
パディングして直す必要があったり色々大変でした。
このクラスは、完成というには程遠いですが
(元の進数と圧縮に使う進数をsetするメソッドすらない…)
ニーズがあるか知りたいので公開してしまいます。
自由に使ってもらって構いませんが、無保証です。
うん、後で弊社の小川に説教されるかも…
手が空いたら、今度は自分でconvert処理を書くかもしれません。
base_convertでは36進数が限界で62進数が使えないので。
「クラス」といえば
アシアルのスクールにオブジェクト編が追加されました。
PHPオブジェクト指向編 効率的にプログラミングする手法
講師の小川の方が丁寧に正しいオブジェクトの書き方を教えますので、
PHPでオブジェクト使ってみたい方は是非一度ご検討ください。
コメント
コメントフォーム
トラックバック
最近の記事
- もうすぐ健康診断があるんだ・・・ [2010年09月02日 : 阿部恵]
- Photoshopで壁紙を作りながら、基本的な使い方を覚える [2010年09月01日 : 鴨田健次]
- はじめての共同作業 Canvas編 (node.js + websocket) [2010年09月01日 : 中川善樹]
- 「PHP×Flex(後編)」PHPテクニカルセミナー(無料)第4弾の募集を開始しました!! [2010年08月26日 : 和田記光]
- 【HTML5】Canvasでお絵かきしてみた(前編) [2010年08月25日 : 橋本章史]
- MacにgroongaのMySQL用ストレージエンジン [2010年08月23日 : 笹亀弘]
- Appleのサイトで見たiPhone4をFireworksで描いてみました-1/2 [2010年08月19日 : 和田記光]
- iPad版の会社紹介を作ってみました [2010年08月19日 : 小林有佳]
- iPhoneアプリ開発開始時に気をつけるべきファイルの取り扱い (2) [2010年08月19日 : 亀本大地]
- symfonyセミナー動画無料公開! [2010年08月13日 : 岡本雄樹]



base64_encode(pack('H*', $hex));
じゃだめなんですか?
コメントありがとうございます!
検証コードを書いてみましたが、うまく行きました。単純にbase64を掛けるのでなく
packして文字数を減らした上でbase64を掛ければ圧縮できるんですね。
今回は色々と勉強になりました。
●コード
<?php
$hex = '153733d3cd194c6b60e50f1dcf5cdf703a3a363031302d30352d31382036303a33313a3aa9e9a25ea53fb8a2';
$pack = pack('H*', $hex);
$base64 = base64_encode($pack);
var_dump($hex);
var_dump($pack);
var_dump($base64);
var_dump(unpack('H*', base64_decode($base64)));
●出力結果
string(88) "153733d3cd194c6b60e50f1dcf5cdf703a3a363031302d30352d31382036303a33313a3aa9e9a25ea53fb8a2"
string(44) "73Lk`::6010-05-18 60:31::"
string(60) "FTcz080ZTGtg5Q8dz1zfcDo6NjAxMC0wNS0xOCA2MDozMTo6qemiXqU/uKI="
array(1) {
[1]=>
string(88) "153733d3cd194c6b60e50f1dcf5cdf703a3a363031302d30352d31382036303a33313a3aa9e9a25ea53fb8a2"
}