1. 導入: なぜCANON_EQが重要なのか
Javaで正規表現を扱う際、私たちは通常、半角英数字や特定の記号を対象にパターンマッチングを行います。しかし、多言語対応やUnicode環境下では、「見た目は同じだが、内部的なバイト列が異なる」というケースに遭遇します。例えば、濁点付き文字(「が」と「か」+「゛」)などです。これらを考慮せずにマッチングを行うと、ユーザーの意図に反して「一致しない」というバグを生みます。この課題を解決し、Unicode正規化を意識したマッチングを実現するのが「CANON_EQ」フラグです。
2. 基礎知識: Unicodeの正規化とCANON_EQ
Unicodeには、同じ文字を表現するのに複数の符号化方式が存在します。
・合成済み文字: 「が」のように、一つのコードポイントで表現されるもの。
・分解文字: 「か」と「濁点」のように、複数のコードポイントの組み合わせで表現されるもの。
これらは視覚的に同一ですが、プログラム上は別物として扱われます。Pattern.CANON_EQフラグを有効にすると、正規表現エンジンは「正規等価性(Canonical Equivalence)」を考慮し、分解された文字であっても合成済み文字と同一であるとみなしてマッチングを行います。
3. 実装/解決策
実装は非常にシンプルで、Pattern.compileメソッドの第2引数にPattern.CANON_EQを指定するだけです。これにより、特別なロジックを組むことなく、内部的に正規化された状態での比較が可能になります。Named groups(名前付きキャプチャグループ)と組み合わせることで、可読性の高いコードと柔軟なバリデーションを両立できます。
4. サンプルプログラム
以下のコードは、分解された文字と合成済み文字の両方に対して、CANON_EQを用いてマッチングを行う例です。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexCanonEqExample {
public static void main(String[] args) {
// 「が」の合成済み文字
String target1 = “\u304C”;
// 「か」+「゛」の分解文字
String target2 = “\u304B\u3099”;
// 名前付きグループを使用してパターンを定義
String regex = “(?
// CANON_EQを指定してコンパイル
Pattern pattern = Pattern.compile(regex, Pattern.CANON_EQ);
checkMatch(pattern, target1, “合成済み文字”);
checkMatch(pattern, target2, “分解文字”);
}
private static void checkMatch(Pattern pattern, String input, String label) {
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
// マッチしたグループの内容を出力
System.out.println(label + “が一致しました: ” + matcher.group(“targetChar”));
} else {
System.out.println(label + “は一致しませんでした。”);
}
}
}
5. 応用・注意点
現場でCANON_EQを使用する際には、以下の2点に注意してください。
・パフォーマンスへの影響: CANON_EQを有効にすると、マッチング時に内部的な正規化処理が走るため、通常のフラグなしの比較に比べてパフォーマンスが低下します。大量のテキストを処理するループ内での多用は避けるべきです。
・正規化の事前実行: 可能であれば、入力値を受け取った直後の段階で「java.text.Normalizer」クラスを使用して、文字列全体をNFC(正規化形式C)に統一しておくのがベストプラクティスです。正規表現側に頼りすぎず、データ層で正規化を完結させる方が、アプリケーション全体の保守性は向上します。
このフラグは、多言語対応の検索機能やユーザー入力バリデーションにおける「隠れた不一致」を防ぐ強力なツールとなります。ぜひ活用してください。

コメント