最近仕事で文字コード関連のことを検討していて、改めてプログラマのための文字コード技術入門を読み返していました。そこで「正規化というやつが必要では?」と気付いたので少し調べてみました。
ここで言う正規化とは?
例えばUnicodeで"ぱ"はU+3071で表すことができる一方、"は"(U+306F)と丸(U+309A)の合成でも表現できます。もしも後者の文字をブラウザ等に表示すると、「一見"ぱ"のように見えるが検索ではヒットしない」などの問題が出るため、符号化表現を一通りに揃えることが有効です。これを正規化と言うそうです。
実験環境
- Personal Environment 11.15.0
- DBMSはSQL Server
- Service Studio 11.53.5
- 久しぶりに仕事以外でService Studioを使ったら自動アップデートされていてUIがかなり変わっていました
実験内容
前提
- 取り扱う文字列は"あ~あ①髙島屋パバぱ"とします。10文字のように見えますが、この中の最後の3文字"パバぱ"は合成した文字なため実際は13文字です
- 正規化はExtesionで行いました。Extensionの中でString.Normalize メソッドを呼び出しています。
NormalizationForm.FormC
を利用し、合成済の文字に寄せる方向としました。
正規化する/しないにより文字数は変わるのか?(OutSystems)
OutSystems組み込みのLength()を使って確認してみました。結果、正規化する/しないに関わらず10文字と判定されました(不思議)
→StringInfo.LengthInTextElementsが使われているのかもしれません。
正規化する/しないにより文字数は変わるのか?(C#)
正規化を行うExtensionの中でString.Lengthを確認してみました。結果、正規化する場合は10文字、しない場合は13文字と判定されました(納得の結果)。
正規化せずに長さ10のEntity Attributeに登録できるのか?
"String or binary data would be truncated." のエラーが発生しました。そのためやはり事前の正規化が必要そうです。
Entityに登録された正規化した文字列、正規化していない文字列に差はあるのか?
Attribute like "%ぱ%"
で検索すると、正規化した"ぱ"も正規化していない"ぱ"もヒットしました(不思議)
Entityから取り出したデータに変化はあるのか?
「正規化せずに登録したデータをEntityから取り出すと勝手に正規化されていたりしないか?」確認しましたが、特に変化はなく杞憂に終わりました。
番外編:Unicodeエスケープは入力可能か?
残念ながらText型の変数に"\u2660"などと入力しても認識してくれませんでした。
番外編:本当にブラウザはうまく検索してくれないのか?
正規化した文字列、正規化していない文字列を取得するREST API Methodを作成しブラウザで表示しました。ブラウザ(Chrome)の検索機能を使い"ぱ"を検索すると、正規化したものもしていないものもどちらもヒットしました(本に書かれていたことと違う)。少し古い本なので技術が発展して課題解決されたのかもしれません。
結論
OutSystems上では正規化してもしなくても文字列の長さが同じだったり、Entityの検索でもどちらもヒットしたりで、正規化しなくていいかもとも思いましたが、正規化していない場合Entity登録の際にエラーが発生する可能性があるため、以下のように初めに正規化を行おうと思います(これを行っていないとサーバーサイドバリデーションをすり抜けてEntityに登録しようとし、そこでエラーが発生してしまう)。
- 正規化
- サーバーサイドバリデーションの一環で最大文字数を超えていないか確認
- Entityに登録
ただ、正規化すると一部文字("Å"(U+212B)など)は別の文字に変わる、またその文字をShift_JISに変換しようとするとJIS X 0208には該当文字が存在しないため文字化けが発生するなどの背反もあるので「いつも正規化が正しい」わけではなさそうです。