MW211 EXIT

devlog
PHP/マルチバイト文字数
2014年04月09日
マルチバイト文字の文字数等を取得する関数は以下の通り。

【UTF-8】マルチバイト文字はだいたい1文字あたり3バイト分を使用
┌────────────┬────────────┬────────────┐
│         文字数         │         文字幅         │        バイト数        │
├────────────┼────────────┼────────────┤
│mb_strlen('a')       →1│mb_strwidth('a')     →1│strlen('a')          →1│
├────────────┼────────────┼────────────┤
│mb_strlen('あ')      →1│mb_strwidth('あ')    →2│strlen('あ')         →3│
├────────────┼────────────┼────────────┤
│mb_strlen('aあ')     →2│mb_strwidth('aあ')   →3│strlen('aあ')        →4│
├────────────┼────────────┼────────────┤
│mb_strlen('①')      →1│mb_strwidth('①')  →1★│strlen('①')         →3│
└────────────┴────────────┴────────────┘

【SJIS-win(シフトJIS(CP932))】マルチバイト文字は1文字あたり2バイト分を使用
┌────────────┬────────────┬────────────┐
│         文字数         │         文字幅         │        バイト数        │
├────────────┼────────────┼────────────┤
│mb_strlen('a')       →1│mb_strwidth('a')     →1│strlen('a')          →1│
├────────────┼────────────┼────────────┤
│mb_strlen('あ')      →1│mb_strwidth('あ')    →2│strlen('あ')         →2│
├────────────┼────────────┼────────────┤
│mb_strlen('aあ')     →2│mb_strwidth('aあ')   →3│strlen('aあ')        →3│
├────────────┼────────────┼────────────┤
│mb_strlen('①')      →1│mb_strwidth('①')  →1★│strlen('①')         →2│
└────────────┴────────────┴────────────┘

問題は「mb_strwidth()」における記号(「①」など)の扱い(★)。
半角(1バイト幅)扱いで算出されてしまうのだ。

これは仕様で文字コードの帯域によって、以下のように定義されているからだ。
┌───────┬─────┐
│U+0000~U+0019│ 0バイト幅│
├───────┼─────┤
│U+0020~U+1FFF│ 1バイト幅│
├───────┼─────┤
│U+2000~U+FF60│ 2バイト幅│
├───────┼─────┤
│U+FF61~U+FF9F│ 1バイト幅│
├───────┼─────┤
│U+FFA0~      │ 2バイト幅│
└───────┴─────┘

ということで、全角文字なのに、半角扱いされてしまう文字を当てはめてみると…。
┌───────┬─────┐
│U+0000~U+0019│ 0バイト幅│
├───────┼─────┤
│U+0020~U+1FFF│ 1バイト幅│ω(U+03C9)
├───────┼─────┤
│U+2000~U+FF60│ 2バイト幅│※(U+203B)、①(U+2460)、★(U+2605)、℃(U+2103)
├───────┼─────┤
│U+FF61~U+FF9F│ 1バイト幅│
├───────┼─────┤
│U+FFA0~      │ 2バイト幅│
└───────┴─────┘
ん?辻褄が合わない?

さて、こんな挙動が不審な「mb_strwidth()」対策は?
思い切って使うのを止めてしまう(安定するまで)。

シフトJISならバイト数=文字幅なので、これを利用して、
シフトJISに変換してバイト数を求めるのが無難なやり方みたい。
で、変換できない(「?」になる)UTF-8特有文字とかの対策は?ってことになるが
ま、そいつらは黙殺するしかないか。
分類:PHP