[Java][文字コード] Unicode 正規化と、文字列の正規表現と関数を用いた置換
Java の標準ライブラリに java.text.Normalizer という Unicode 正規化を行うクラスがあります。
濁点・半濁点を合成・分解したりするのに便利なのですが、JIS X 0208 の「Å(U+212B)」を「上リング付きA(U+00C5)」に変換したり、JIS X 0213 のギリシャ文字のαやεにアクセントが付いた U+1F71、U+1F73 を U+03AC、U+03AD に変換するなど、余計な変換を行うため、JIS のコードに戻せなくなってしまいます。
そこで、ひらがな・カタカナだけを正規化することができないか考えてみました。
正規表現でひらがな・カタカナを検索し、マッチした文字列だけ正規化します。
最初、合成と分解をそれぞれ実装したのですが、同じようなロジックになったので、Java8 の関数を用いて共通化してみました。
まず、文字列を正規表現で検索し、マッチした文字列を変換するメソッドを作成します。
/** * 文字列に含まれるパターンを関数を使って置換します。 * * @param s 文字列 * @param p パターン * @param f 関数 * @return 変換後の文字列 */ static String replaceAll( CharSequence s, Pattern p, Function<String, String> f) { Matcher m = p.matcher(s); int length = s.length(); StringBuilder sb = new StringBuilder(length); int index = 0; while (m.find()) { // マッチしなかった部分をそのまま追加。 sb.append(s.subSequence(index, m.start())); // マッチした部分を変換して追加。 sb.append(f.apply(m.group())); index = m.end(); } // 残りをそのまま追加。 sb.append(s.subSequence(index, length)); return sb.toString(); }
これを使えば、濁点・半濁点の合成・分解のコードは簡単に実装できました。
/** * 全角カナの正規表現。 */ static final Pattern FULLWIDTH_KANA = Pattern.compile("[\u3041-\u309A\u30A1-\u30FA]+"); /** * 文字列に含まれる基底文字と結合文字を合成します。 * * @param s 文字列 * @return 合成された文字列 */ static String composeFullwidthKana(CharSequence s) { return replaceAll(s, FULLWIDTH_KANA, (g) -> Normalizer.normalize(g, Normalizer.Form.NFC)); } /** * 文字列に含まれる合成済み文字を基底文字と結合文字に分解します。 * * @param s 文字列 * @return 分解された文字列 */ static String decomposeFullwidthKana(CharSequence s) { return replaceAll(s, FULLWIDTH_KANA, (g) -> Normalizer.normalize(g, Normalizer.Form.NFD)); }