🐙

【PHP】拡張書記素クラスターを含む文字列の長さを正規表現で求める

2023/07/07に公開

intl モジュールの grapheme_strlen が使えない環境で正規表現(PCRE、Oniguruma)を代わりに使う場合、どのくらい速度が違うのか気になったので、計測することにした。

grapheme_strlenpreg_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