PR

[PHP] n進数からm進数へ相互変換する

通常、進数変換は2進数を10進数、16進数を2進数にするなど数学的な計算に用いられる。それ以外であればbase64がメールで利用されている。これはマルチバイト文字をアルファベットなどに置き換えて送信し、受信側が再度組み立てるもの。なぜこの手順が必要かというと、メールでマルチバイト(日本語)文字などは直接送信できないため。

最近ではTwitterなど文字制限があるものに対して長いURLを投稿することが難しいため、URL短縮を目的とした利用や需要が高まっている。YouTubeは62進数を利用しているのではないかという記事が参考文献内にある。(10進数が0-9、16進数が0-9A-F、36進数が0-9A-Z、62進数が0-9A-Za-z、64進数が0-9A-Za-z+/、URLに64進数を使うと記号部をエンコードする必要がある、代わりに「-」「.」「!」を利用する方法もあるらしい)

一般的にはURL自体を圧縮するのではなく、データベース内のキー情報、例えばint型のカラムidが整数値であることを利用して、この数値を圧縮してURL転送するような仕掛けになっているようだ。

PHPで16新数を10進数に

HexDec("ff"); // -> 255

PHPで2-36進数を相互変換

string base_convert ( string $number , int $frombase , int $tobase )

PHPで2-36と62進数を変換

スポンサードリンク

echo $res = BaseConvert::convert("99999999999999",10,36); echo "\n"; // "zg3d62r5r"
echo BaseConvert::convert($res,36,10); echo "\n"; // "99999999999999"

echo $res = BaseConvert::convert("99999999999999",10,62); echo "\n"; // "SOYTZ0AR"
echo BaseConvert::convert($res,62,10); echo "\n"; // "99999999999999"

※参考文献内にあるBaseConvert.inc.zipのコードを参照(以下一部抜粋)

function _from62($number, $frombase, $tobase)
 {
 static $CHARLEN = 62;
 static $FROM62TABLE = array(
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0,
 0, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0
 );

 $num = 0;
 for ($i = 0; isset($number[$i]); $i++) {
 $num *= $CHARLEN;
 $num += $FROM62TABLE[ord($number[$i]) & 0x7f];
 }

 $num = sprintf("%.0f", $num);
 if($tobase != 10) $num = base_convert($num, 10, $tobase);

 return $num;
 }

 function _to62($number, $frombase, $tobase)
 {
 static $CHARLIST = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
 static $CHARLEN = 62;

 if($frombase != 10) $number = base_convert($number, $frombase, 10);

 $dst = '';
 while ($number) {
 $dst = $CHARLIST[fmod($number, $CHARLEN)] . $dst;
 $number = floor($number / $CHARLEN);
 }

 return strval($dst);
 }

perlでの実装

※0-9a-zの36進数、14桁程度まで利用できる、参考文献より

use strict;
my $base_str = "0123456789abcdefghijklmnopqrstuvwxyz";
my $base_36 = convert_36(1234567);
print "$base_36\n";
my $base_10 = convert_10($base_36);
print "$base_10\n";

sub convert_36 {
my $number = shift;
my @work;
while ($number > 0) {
unshift @work, substr($base_str, $number % 36, 1);
$number = int($number / 36);
}
return join('', @work);
}

sub convert_10 {
my @work = reverse split //, shift;
my $number;
foreach my $idx (0 .. $#work) {
$number += index($base_str, $work[$idx]) * (36 ** $idx);
}
return $number;
}

参考文献

コメント