Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

예제로 배우는 Java Pattern 클래스의 CANON_EQ 필드

Pattern.CANON_EQ는 Java 정규 표현식 엔진에서 두 문자를 정준적으로(canonical) 동일한 경우에만 일치시키도록 지정하는 필드입니다. Pattern.compile() 메서드에 이 상수를 플래그 값으로 전달하면, 두 문자의 완전한 정준 분해(full canonical decomposition) 결과가 서로 같을 때에만 일치로 처리됩니다.

여기서 정준 분해(canonical decomposition)란 유니코드 텍스트 정규화(normalization) 형식 중 하나로, 결합 문자를 기본 문자와 분리된 형태로 표현하는 방식입니다. 예를 들어 문자 'å'(U+00E5)는 기본 문자 'a'와 그 위에 붙는 고리 모양 결합 문자(U+030A)의 조합으로 분해할 수 있습니다. CANON_EQ 플래그를 사용하면 이처럼 분해 결과가 같은 문자들을 동일하게 취급합니다.

예제 1: 정준적으로 동일한 문자 일치시키기

아래 예제는 'b'와 위에 붙는 점 결합 문자(U+0307)로 구성된 패턴을 만들고, 점이 포함된 한 글자 문자 'ḃ'(U+1E03)와 비교합니다.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CANON_EQ_Example {
   public static void main(String args[]) {
      // 'b' + 결합 문자 U+0307(위에 붙는 점)로 구성된 정규 표현식
      String regex = "b\u0307";
      // CANON_EQ 플래그를 적용하여 정규 표현식 컴파일
      Pattern pattern = Pattern.compile(regex, Pattern.CANON_EQ);
      // Matcher 객체 생성
      Matcher matcher = pattern.matcher("\u1E03"); // 'ḃ'
      if (matcher.matches()) {
         System.out.println("일치하는 항목을 찾았습니다.");
      } else {
         System.out.println("일치하는 항목이 없습니다.");
      }
   }
}

실행 결과

일치하는 항목을 찾았습니다.

'ḃ'(U+1E03)를 정준 분해하면 'b' + U+0307이 됩니다. 따라서 CANON_EQ 플래그 덕분에 두 표현이 동일한 문자로 인식되어 일치 결과가 출력됩니다.

예제 2: 여러 입력값 비교하기

이번에는 'a'와 고리 결합 문자(U+030A)로 구성된 패턴을 다양한 형태의 입력 문자열과 비교해 봅니다.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CANON_EQ_Example {
   public static void main(String args[]) {
      // 'a' + 결합 문자 U+030A(위에 붙는 고리)로 구성된 정규 표현식
      String regex = "a\u030A";
      // CANON_EQ 플래그를 적용하여 정규 표현식 컴파일
      Pattern pattern = Pattern.compile(regex, Pattern.CANON_EQ);
      // 다양한 형태의 입력 문자열 배열
      String[] input = {"\u00E5", "a\u0311", "a\u0325", "a\u030A", "a\u1E03", "a\uFB03"};
      for (String ele : input) {
         Matcher matcher = pattern.matcher(ele);
         if (matcher.matches()) {
            System.out.println(ele + " 은(는) " + regex + " 패턴과 일치합니다.");
         } else {
            System.out.println(ele + " 은(는) " + regex + " 패턴과 일치하지 않습니다.");
         }
      }
   }
}

실행 결과

å 은(는) a\u030A 패턴과 일치합니다.
a\u0311 은(는) a\u030A 패턴과 일치하지 않습니다.
a\u0325 은(는) a\u030A 패턴과 일치하지 않습니다.
a\u030A 은(는) a\u030A 패턴과 일치합니다.
a\u1E03 은(는) a\u030A 패턴과 일치하지 않습니다.
a\uFB03 은(는) a\u030A 패턴과 일치하지 않습니다.

결과 해석

  • \u00E5 (å) — 정준 분해 결과가 'a' + U+030A이므로 패턴과 일치합니다.
  • a + \u0311 — 결합 문자가 다른(위쪽 역브레브) 형태이므로 일치하지 않습니다.
  • a + \u0325 — 고리가 아래에 붙은 형태이므로 일치하지 않습니다.
  • a + \u030A — 패턴과 완전히 동일하므로 당연히 일치합니다.
  • a + \u1E03 — U+1E03은 '점이 찍힌 b(ḃ)'이므로 일치하지 않습니다.
  • a + \uFB03 — U+FB03은 합자(ligature) ffi로, 정준 분해 결과가 다르므로 일치하지 않습니다.

정리

CANON_EQ 플래그는 화면상으로는 같아 보이지만 내부 코드가 다른 유니코드 문자들을 올바르게 비교해야 할 때 매우 유용합니다. 다만 정준 분해 연산이 추가로 수행되므로 일반적인 매칭 방식보다 성능 비용이 클 수 있다는 점은 참고해야 합니다.