PHP の
range
関数の範囲を平仮名・カタカナ・漢字にまで対応させたい。
つまり range
のマルチバイト版 mb_range
関数が欲しい。こんなの↓
$a = mb_range( 'あ', 'お' );
print_r( $a );
Array
(
[0] => あ
[1] => ぃ
[2] => い
[3] => ぅ
[4] => う
[5] => ぇ
[6] => え
[7] => ぉ
[8] => お
)
- 「こんなの何に使うの?」という方は TL; DR 参照
TS; DR(マルチバイト対応版range()
)
UTF-8 の文字コード表の順になりますが、平仮名だけでなく漢字も範囲対象にして一気に得ることができます。
<?php
/**
* range('A', 'Z') のマルチバイト対応版
*
* @author Rodney Rehm (https://gist.github.com/rodneyrehm/1306118)
* @url Latest code https://qiita.com/KEINOS/items/f0aa3cf476455390d95f
*
* @param string $start 開始文字
* @param string $end 終了文字
* @return array 開始から終了までの文字の配列
*/
function mb_range($start, $end)
{
// $start と $end が同じ場合は何もしない
if ($start == $end) {
return array($start);
}
$_result = array();
// $start と $end のユニコードを取得
list(, $_start, $_end) = unpack("N*", mb_convert_encoding($start . $end, "UTF-32BE", "UTF-8"));
// 処理方向を検知する
$_offset = $_start < $_end ? 1 : -1;
$_current = $_start;
// BOM なし UTF-32
while ($_current != $_end) {
$_result[] = mb_convert_encoding(pack("N*", $_current), "UTF-8", "UTF-32BE");
$_current += $_offset;
}
$_result[] = $end;
return $_result;
}
- オンラインで動作を見る @ paiza.IO
TS; DR(mb_range
の使いみち)
range
関数、mb_range
ユーザ関数とは
PHP の range
関数はrange(1,9);
を [1,2,...,9]
と、指定した範囲の整数の配列を作成してくれます。また、数値だけでなく range('A','Z');
-> ['A','B',...,'Z']
のように ASCII コード表の範囲も取得できます。
mb_range
ユーザー関数は、この range()
をマルチバイト対応させて「mb_range( 'あ', 'ん' )
」などとして UTF-8 の文字コード表内の指定範囲を配列で得たい人のためのユーザー関数です。
何に使うの?
何に使うのか。ご覧の通り「あ,い,う,え,お」と並ばないため余り用途はないかもしれません。「あ,い,う,え,お」と並ばせたい場合は preg_match
などを利用した方が賢いかもしれません。
mb_range()
の使いみちの1つとしては、文字列の長さの圧縮に使えます。つまり文字列を短くできるということです。(データの圧縮ではありませんので注意)
アルファベットが足りないなら漢字を使えばいいじゃない
0-9
は 10 文字で 10 進数を表現します。16 進数の場合は 0-9
a-f
の 16 文字です。同様に 0-9
a-z
A-Z
+
/
の 64 文字(62文字+記号2文字)を使えば 64 進数になります。「進数」ということは1桁で、その文字数ぶんを表現できるということです。逆に言えば ASCII の場合は可視化できる範囲は !
〜 ~
までの 92 文字です。
しかし、時代は UTF-8 標準。そう、漢字まで使っちゃえば、数値の桁数をより短くできるということです。
数値といえば、「長ければ長いほど良いが、できれば短くしたい」の代表がハッシュ関数によるハッシュ値の長さです。しかし、大量の文字列を配列に入れるのが面倒だったので range
関数のマルチバイト版を探していたら GitHub の gist で公開されていた方いらしたたので利用させていただきました。
- mb_range by Rodney Rehm @ Gist
- MD5 ハッシュ関数を10桁に短く漢字で圧縮 @ KEINOS blog
<?php
require('mb_base_encode.php');
require('mb_range.php');
// Input string
$string = 'This is a sample string to be hashed.';
// Output
echo 'Original String: ', $string, PHP_EOL;
echo 'MD5 hash raw : ', hash('md5', $string), PHP_EOL;
echo 'MD5 hash short : ', mb_base_encode(hexdec(hash('md5', $string)));
Original String: This is a sample string to be hashed.
MD5 hash raw : baad33e1e97f316b9750c27c86bf64d6
MD5 hash short : 䙔倁屩劷䋾彨䏔䂌䤘剒
<?php
/**
* Returns base Nth encoded string from decimal number input.
*
* @param integer $number Decimal number to encode
* @return string Encoded string
*/
function mb_base_encode(integer $number): string
{
$char = array_merge(
mb_range('䀀', '䶵'), // U+4000 - U+4DB5
mb_range('一', '俿'), // U+4E00 - U+4FFF
mb_range('倀', '忿') // U+5000 - U+5FFF
// REF: http://www.utf8-chartable.com/unicode-utf8-table.pl?start=16384&number=1024
);
$base = count($char);
$result = "";
while ($number > 0) {
$result = $char[ fmod($number, $base) ] . $result;
$number = floor($number / $base);
}
return ( $result == "" ) ? 0 : $result;
}
<?php
function mb_range(string $start, string $end): array
{
// if start and end are the same, well, there's nothing to do
if ($start == $end) {
return array($start);
}
$_result = array();
// get unicodes of start and end
list(, $_start, $_end) = unpack("N*", mb_convert_encoding($start . $end, "UTF-32BE", "UTF-8"));
// determine movement direction
$_offset = $_start < $_end ? 1 : -1;
$_current = $_start;
while ($_current != $_end) {
$_result[] = mb_convert_encoding(pack("N*", $_current), "UTF-8", "UTF-32BE");
$_current += $_offset;
}
$_result[] = $end;
return $_result;
}
- オンラインで動作をみる @ paiza.IO