Writing Appropriately

職務経歴

PHP MySQL REGEXPでの人名検索のための文字列変換

高崎髙﨑を検索する際にまたを意識しなくても良いようMySQL REGEXP検索できるよう変換。

どちらのタカサキでもSELECT * FROM users WHERE user_nm REGEXP '[高|髙][崎|﨑]'となるようにする。

という処理を書いた後、ふと自分の変換処理の書き方が良いコードなのか疑問に思ったため速度を調べた。


入力文字列

浜曻﨑髙熙曻德䑓壱二

出力文字列

[濱|濵|浜][曻|昇][﨑|崎][髙|高][煕|熙][曻|昇][德|徳][䑓|台][1|1|一|壱][2|2|二|弐]

文字のグループ化

<?php
function convert_regexp_search($func, $str) {
    $str = $func(array('濱', '濵', '浜'), $str);
    $str = $func(array('﨑', '崎'), $str);
    $str = $func(array('髙', '高'), $str);
    $str = $func(array('煕', '熙'), $str);
    $str = $func(array('曻', '昇'), $str);
    $str = $func(array('德', '徳'), $str);
    $str = $func(array('䑓', '台'), $str);
    $str = $func(array('1', '1', '一', '壱'), $str);
    $str = $func(array('2', '2', '二', '弐'), $str);
    $str = $func(array('3', '3', '三', '参'), $str);
    
    // 続く
    
    return $str;
}

変換処理候補1 mb_substrで一文字毎に処理

<?php
$funcA = function ($group, $str, $encoding = "UTF-8") {
    $r = array();
    $to = '['.implode('|', $group).']';
    $keys = array_flip($group);
    $cnt = mb_strlen($str, $encoding);
    for($i = 0; $i < $cnt; $i++) {
        $s = mb_substr($str, $i, 1, $encoding);
        if (array_key_exists($s, $keys)){
            $r[] = $to;
        }
        else {
            $r[] = $s;
        }
    }
    return implode('', $r);
};

変換処理候補2 preg_splitで一文字毎に分解

<?php
$funcB = function ($group, $str) {
    $r = array();
    $to = '['.implode('|', $group).']';
    $keys = array_flip($group);
    $arr = array_slice(preg_split("//u", $str), 1, -1);
    foreach ($arr as $s) {
        if (array_key_exists($s, $keys)){
            $r[] = $to;
        }
        else {
            $r[] = $s;
        }
    }
    return implode('', $r);
};

変換処理候補3 str_replaceで一度別の文字にして変換

<?php
$funcC = function ($group, $str, $tmp = "\t") {
    $to = '['.implode('|', $group).']';
    $str = str_replace($group, $tmp, $str);
    $str = str_replace($tmp, $to, $str);
    return $str;
};

1万回ループでの速度計測

<?php
$functions = array('A'=>$funcA, 'B'=>$funcB, 'C'=>$funcC);
foreach ($functions as $key => $func) {
    $start = microtime(true);
    for ($i = 0; $i < 10000; $i++ )  {
        convert_regexp_search($func, $str);
    }
    $time = microtime(true) - $start;
    echo "{$key}: {$time}\n";
}

計測結果

A: 1.9553890228271 秒
B: 0.86666202545166 秒
C: 0.068694114685059 秒

一言

当然の結果であった。