##概要
PHPでバイト数単位で文字列を切り出す際に、思いつくのはsubstrを利用する方法だろう。
このとき半角文字列のみや全角文字列のみを対象に処理をする場合など、1文字あたりのバイト数が同じであれば問題は生じない。
しかし全角半角混在の文字列など1文字あたりのバイト数が異なる文字列をバイト数単位で切り出す際には注意が必要だ。
##問題点
マルチバイトの文字列をsubstrで半端なバイト数単位で切り出すと、
文字列を中途半端な位置で切り出してしまい、不具合の温床となる。
##具体例
文字コードSJISにおいて、
Aあいうえお
という文字列があったとする。(Aは半角、それ以外は全角)
この文字列をsubstrで6バイトで切り出すと、
全角文字(2バイト文字)「う」の1バイト目だけを切り出してしまい文字列が壊れてしまう。
######テストコード
<?php
$target = "Aあいうえお";// 対象文字列
$byte = 6; // 分割単位: byte
$enc = "SJIS"; // 文字コード
$result = ""; // 処理結果
$nl = "<br/>"; // 改行用
// 切り出し処理実行
$result = substr($target, 0 , $byte);
// 結果の文字数を取得
$len_mb = mb_strlen($result, 'SJIS');
// 結果のバイト数を取得
$len = strlen($result);
echo "対象文字列 = ".$target.$nl;
echo "切出バイト数 = ".$byte.$nl;
echo "結果文字列 = ".$result.$nl;
echo "結果文字数 = ".$len_mb.$nl;
echo "結果バイト数 = ".$len.$nl;
?>
######テスト結果
対象文字列 = Aあいうえお
切出バイト数 = 6
結果文字列 = Aあい
結果文字数 = 4 ←表示できない半端なコードが存在することを示している
結果バイト数 = 6
「Aあい」のあとにマルチバイト文字列の断片コードが残ってしまい不具合の原因になってしまう。
##解決方法
substr関数の代わりにmb_strcut関数を使用することで、
1文字に満たない半端なバイトコードを間引いてくれる。
######テストコード
<?php
$target = "Aあいうえお";// 対象文字列
$byte = 6; // 分割単位: byte
$enc = "SJIS"; // 文字コード
$result = ""; // 処理結果
$nl = "<br/>"; // 改行用
// 切り出し処理実行
$result = mb_strcut($target, 0 , $byte, $enc);
// 結果の文字数を取得
$len_mb = mb_strlen($result, 'SJIS');
// 結果のバイト数を取得
$len = strlen($result);
echo "対象文字列 = ".$target.$nl;
echo "切出バイト数 = ".$byte.$nl;
echo "結果文字列 = ".$result.$nl;
echo "結果文字数 = ".$len_mb.$nl;
echo "結果バイト数 = ".$len.$nl;
?>
######テスト結果
対象文字列 = Aあいうえお
切出バイト数 = 6
結果文字列 = Aあい
結果文字数 = 3 ←表示されている文字列と同じ文字数
結果バイト数 = 5
phpのmb_strcut関数を利用することで
6バイト目の文字列は「う」の1バイト目になってしまうため、
半端なバイト数を間引いて、ひとつ前までの切り出しとする結果を得ることができる。