Unicode文字列の正規化(Javaの場合)

いわゆる半角カナを使うと目くじらを立てて怒る人がいたのも今は昔。Unicodeのおかげで、半角カナはふつうの文字になりました。とはいえ、情報を流通させるという観点から言うと、「ページ」と「ページ」、「㌻」が混在しているのは好ましいことではありません(濁点・半濁点も文字として登録されているので、バリエーションはもっと多いです)。

めんどくさそうな問題ですが、Unicodeには正規化という仕掛けが用意してあるので、実は簡単に解決できます(正規化すると、同じものとして扱えます)。

その方法を、「PHPにおけるUnicode文字列の正規化」という記事で解説しました。PHPは単なる例であって、他の言語でも簡単です。ここでは、Javaの場合を紹介しましょう(1.6以降限定です)。.NETの人はString.Normalizeメソッドをどうぞ。

正規化には、クラスjava.text.Normalizerのメソッドnormalizeを使います。正規化の方法は、列挙型java.text.Normalizer.Formで定義されています。「Normalizer.normalize("㌻",Form.NFC)」のように使います。

解説記事でやっていることをJavaで書くと、次のようになります。

import java.util.*;
import java.text.Normalizer;
import java.text.Normalizer.Form;

public class NormalizerSample {
  private static String codePoints(String str){
    String[] result=new String[str.length()];
    for(int i=0;i<result.length;++i)
      result[i]=Integer.toHexString(str.codePointAt(i));
    return Arrays.toString(result);
  }
  
  public static void main(String[] args) {
    String s="\u30da";
    System.out.printf("「%s」の長さは%d\n",s,s.length());//1になる
    
    System.out.println("「ページ」のコードポイントは、"+codePoints("ページ"));
    
    String[] strs={
        "\u30da",//ペ
        "\u30d8\u309a",//ヘ●
        "\u30d8\u309c",//ヘ゜
        "\uff8d\uff9f",//ペ
        "\uff8d\u309a",//ヘ●
        "\uff8d\u309c",//ヘ゜
        "\u30d8\uff9f",//ペ
        "\u333b"//㌻
    };
    
    for(String str:strs){
      System.out.printf("\n「%s」(%s)の結果\n",str,codePoints(str));
      for(Form type:Form.values()){
        String result=Normalizer.normalize(str,type);
        System.out.printf("%s\t%s\t%s\n",type,result,codePoints(result));
      }
    }
  }
}

追記:正規化が生まれた背景について、小形克宏さんが「なぜUnicode正規化は生まれたか」という解説を書かれています。

小形さんの記事への安岡さんのコメント:人名用漢字と互換漢字

小形さんの回答:芝野さん、安岡さんへのお答え

濫用注意:miで行われているUnicode正規化について

お約束ですが、文字コードについて基本から学びたいという方には、拙著『Webアプリケーション構築入門 実践!Webページ制作からマッシュアップまで 』(森北出版, 2011)がおすすめです。