🐙
【PHP】拡張書記素クラスターを含む文字列の長さを正規表現で求める
intl モジュールの grapheme_strlen
が使えない環境で正規表現(PCRE、Oniguruma)を代わりに使う場合、どのくらい速度が違うのか気になったので、計測することにした。
grapheme_strlen
、preg_match_all
、mbstring の関数が次の漢字を正しく数えられるのかを確認します。U+E0100 は異体字セレクターです。
$str = "竈門禰\u{E0100}豆子";
var_dump(
5 === grapheme_strlen($str),
5 === grapheme_count($str),
5 === grapheme_count2($str)
);
function grapheme_count(string $str): int
{
return preg_match_all('/\X/su', $str);
}
function grapheme_count2(string $str): int
{
mb_ereg_search_init($str, '\X');
$length = 0;
while (mb_ereg_search()) {
++$length;
}
return $length;
}
実行結果は次のようになります。
> php test.php
bool(true)
bool(true)
bool(true)
次に計測してみましょう。
$str = "竈門禰\u{E0100}豆子";
$timer = timer([
'grapheme_strlen' => function() use($str) { grapheme_strlen($str); },
'preg_match_all' => function() use($str) { grapheme_count($str); },
'mb_ereg_search' => function() use($str) { grapheme_count2($str); }
]);
foreach ($timer as $message => $time) {
echo $message, PHP_EOL, $time, PHP_EOL;
}
function timer(array $callables, int $repeat = 100000): array
{
$ret = [];
$save = $repeat;
foreach ($callables as $key => $callable) {
$start = hrtime(true);
do {
$callable();
} while($repeat -= 1);
$stop = hrtime(true);
$ret[$key] = $stop - $start;
$repeat = $save;
}
return $ret;
}
計測結果は次の通りです。
> php test.php
grapheme_strlen
94997600
preg_match_all
34618900
mb_ereg_search
324994600
Discussion