PHPのmb_convert_kanaとUnicodeのNFKC正規化 2014/11/27
アルファベットや仮名などの文字列に対して検索などの目的で全角半角統一などの正規化をしたい場合に、
PHPでは mb_convert_kana
を使うことがある。同じ目的でJavaなどではUnicodeのNFKC正規化を使うことがある。
NFKC正規化では大雑把にいうと、半角カナは全角カタカナに変換され、全角の英数字や記号は半角に変換される。これはなんとなくPHPの mb_convert_kana($str, 'asKV', 'UTF-8')
と同じになりそうな気がするのであるが、厳密に同じになるわけではない。その違いを調べてみた。
mb_convert_kana
の2つ目の引数はどんな変換をするかを表し、以下の意味がある。
a
- 全角英数字を半角に
s
- 全角スペース(U+3000)を半角(U+0020)に
K
- 半角カタカナを全角カタカナに
V
- 半角カタカナの濁点を全角カタカナに変換する際に前の文字と結合する
mb_convert_kana
関数 | PHP Manual
http://php.net/manual/ja/function.mb-convert-kana.php
NFKC正規化と mb_convert_kana($str, 'asKV', 'UTF-8')
でいくつかの文字をサンプル的に調べたところ、以下のとおりであった。
- アルファベット26文字は同じ結果になり、どちらの方法でも全角英字26文字は半角になる。
- 数字10文字は同じ結果になり、どちらの方法でも全角数字10文字は半角になる。
- 丸数字はNFKCでは半角数字に変換されるが、
mb_convert_kana
では変換されない。 - 全角半角のある記号はどちらもだいたい半角に変換されるが、
" ' \ ~
の4つの全角はmb_convert_kana
では変換されない。この4文字だけ変換されないことはPHPマニュアルにも書いてある。 - 全角仮名の結合文字である濁点(U+3099)・半濁点(U+309A)は、直前の文字が濁音・半濁音になりうる仮名の場合に、NFKCでは前の文字と合成されるが、
mb_convert_kana
では合成されない。 - 全角仮名の結合文字でない単独の濁点(U+309B)・半濁点(U+309C)は、NFKCでは空白(U+0020)が直前に挿入され、濁点・半濁点は結合文字(U+3099, U+309A)に変換されるが、
mb_convert_kana
ではそのままである。 - 半角カタカナの濁点・半濁点は、直前の文字が濁音・半濁音になりうる仮名の場合にNFKCも
mb_convert_kana
もその文字と合成されるが、なりえない仮名の場合には、NFKCでは結合文字の濁点(U+3099)・半濁点(U+309A)になり、mb_convert_kana
では単独の濁点(U+309B)・半濁点(U+309C)になる。
以下は実行例。
英数字の全角半角に関して
$str = "AaAa11\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// 0041 0061 FF21 FF41 0031 FF11
// これをUnicode NFKC正規化すると
// 0041 0061 0041 0061 0031 0031
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// 0041 0061 0041 0061 0031 0031
// これはUnicode NFKC正規化と一致する
丸付き数字に関して
$str = "①➀㉑\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// 2460 2780 3251
// これをUnicode NFKC正規化すると
// 0031 2780 0032 0031
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// 2460 2780 3251
// これはなにも変換されておらず、Unicode NFKC正規化と一致しない
記号に関して
$str = "!\"#\$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F 003A 003B 003C 003D 003E 003F 0040 005B 005C 005D 005E 005F 0060 007B 007C 007D 007E
// これをUnicode NFKC正規化しても変化しない
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F 003A 003B 003C 003D 003E 003F 0040 005B 005C 005D 005E 005F 0060 007B 007C 007D 007E
// これも変化しないので、Unicode NFKC正規化と一致する
$str = "!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// FF01 FF02 FF03 FF04 FF05 FF06 FF07 FF08 FF09 FF0A FF0B FF0C FF0D FF0E FF0F FF1A FF1B FF1C FF1D FF1E FF1F FF20 FF3B FF3C FF3D FF3E FF3F FF40 FF5B FF5C FF5D FF5E
// これをUnicode NFKC正規化すると
// 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F 003A 003B 003C 003D 003E 003F 0040 005B 005C 005D 005E 005F 0060 007B 007C 007D 007E
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// 0021 FF02 0023 0024 0025 0026 FF07 0028 0029 002A 002B 002C 002D 002E 002F 003A 003B 003C 003D 003E 003F 0040 005B FF3C 005D 005E 005F 0060 007B 007C 007D FF5E
// これはUnicode NFKC正規化とは違う
全角ひらがなに関して
$str = "ああ゙あ゛かががか゛\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// U+3042 U+3042 U+3099 U+3042 U+309B U+304B U+304C U+304B U+3099 U+304B U+309B
// これをUnicode NFKC正規化すると
// U+3042 U+3042 U+3099 U+3042 U+0020 U+3099 U+304B U+304C U+304C U+304B U+0020 U+3099
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// U+3042 U+3042 U+3099 U+3042 U+309B U+304B U+304C U+304B U+3099 U+304B U+309B
// これはなにも変換されておらず、Unicode NFKC正規化と一致しない
半角カタカナに関して
$str = "アア゙カガ\n";
echo $str;
// 出力結果をUnicodeコードポイントで表すと
// U+FF71 U+FF71 U+FF9E U+FF76 U+FF76 U+FF9E
// これをUnicode NFKC正規化すると
// U+30A2 U+30A2 U+3099 U+30AB U+30AC
echo mb_convert_kana($str, 'asKV', 'UTF-8');
// 出力結果をUnicodeコードポイントで表すと
// U+30A2 U+30A2 U+309B U+30AB U+30AC
// これはUnicode NFKC正規化と一致しない
// さらにこれをUnicode NFKC正規化すると
// U+30A2 U+30A2 U+0020 U+3099 U+30AB U+30AC
上記実行例は PHP 5.5.3 で確認。