備忘録

mb_substrが遅いので対応策

PHP

MySQLのダンプデータでエスケープ文字の対応をしていたらmb_substrが遅いことが判明その時に対応した内容をメモ

mb_substr

文字列の一部を得る

mb_substr ( string $str , int $start [, int $length = NULL [, string $encoding = mb_internal_encoding() ]] ) : string

//テストデータ1000文字
$value = '1234567890あいうえおかきくけこabcdefghijklmnopqrstuvwxyz・・・';

$nStart = microtime(true);

$len = mb_strlen($value, 'UTF-8');
for ($nIdx = 0; $nIdx < $len; $nIdx++) {
	$char = mb_substr($value, $nIdx, 1, 'UTF-8');
	//処理
}

$nEnd   = microtime(true);
echo  $nEnd - $nStart;
5回テスト
0.0044329166412354
0.0036618709564209
0.0022969245910645
0.0045580863952637
0.0032129287719727

平均
0.00363

preg_split

正規表現で文字列を分割する

preg_split ( string $pattern , string $subject [, int $limit = -1 [, int $flags = 0 ]] ) : array

preg_splitを使ってmb_substrの代替え

//テストデータ1000文字
$value = '1234567890あいうえおかきくけこabcdefghijklmnopqrstuvwxyz・・・';

$nStart = microtime(true);

$arrChar = preg_split('//u', $value, -1, PREG_SPLIT_NO_EMPTY);
foreach ($arrChar as $key=>$row) {
	$char = $row;
	//処理
}

$nEnd   = microtime(true);
echo  $nEnd - $nStart;
5回テスト
0.00013303756713867
0.0001521110534668
0.00013303756713867
0.00014901161193848
0.00014805793762207

平均
0.00014

※結果、文字列から1文字づつ抽出して処理するのは「mb_substr」より「preg_split」を使ったほうが26倍速い結果となりました。

実際にエスケープ文字の対応をしたソースコード

//\(エスケープ)連続
$pos = mb_strpos($row, '\\', 0, 'UTF-8');
if ($pos !== false) {
	$rowBk   = '';
	$value   = '';
	$count   = 0;
	$arrChar = preg_split('//u', $row, -1, PREG_SPLIT_NO_EMPTY);//mb_substrが遅いのでpreg_splitを使用
	foreach ($arrChar as $key2=>$row2) {
		$char = $row2;
		if ($char === '\\') {
			$value .= $char;
			$count++;
		} else {
			if (strlen($value) === 0) {
				$rowBk .= $char;
			} else {
				$value = $value.$char;
				$value = str_replace('\\\\',  '\\',    $value);
				//奇数回
				if (($count % 2) !== 0) {
					if ($char === '\'') {
						//シングルクォーテーション
						$value = str_replace("\'",    "''",    $value);
					} else if ($char === '"') {
						//ダブルクォーテーション
						$value = str_replace('\"',    '"',    $value);
					} else if ($char === 'n') {
						//改行
						$value = str_replace('\n',    "\n",    $value);
					}
				}
				$rowBk .= $value;
				$count  = 0;
				$value  = '';
			}
		}
	}
	$row = $rowBk;
}