|
U+00C7
)를 기본 문자와 조합형 부호를 포함하는 조합 시퀀스로 변환한다.
U+00BD
)을 3개 문자의 시퀀스로 변환한다: 1/2
. Normalizer
클래스
Java SE 6은 이제는 널리 알려진 클래스 java.text.Normalizer
를 제공함으로써 유니코드 텍스트 정규화를 지원한다. 이 클래스는 텍스트를 변환하는 normalize
메소드와 NFC, NFD, NFKC, NFKD 등의 유니코드 정규화 형식을 나타내는 Form
enumeration을 모두 정의한다.
다음은 다양한 유니코드 정규화 형식의 응용 예이다.
예제: NFC
웹 상에서 문서를 출판하고 싶다고 가정하자. Character Model for the World Wide Web 스펙은 웹의 색인, 검색 및 기타 텍스트 관련 기능을 향상시킬 목적으로 데이터를 출판하기에 앞서 정규화를 할 것(조기 정규화)을 권장하고 있다. 또한 이 스펙은 거의 모든 레거시 데이터와 현재의 소프트웨어에 의해 생성된 데이터가 이미 NFC로 정규화되어 있기 때문에 NFC가 선호되고 있다고 기술하고 있다. 다음 코드는 표준 인풋에서 데이터를 읽고 NFC로 정규화된 데이터를 표준 아웃풋에 작성하는 경우이다. 인풋과 아웃풋 모두에 UTF-8 인코딩이 사용된다.
import java.io.*;
import java.text.Normalizer;
import java.text.Normalizer.Form;
public class NFC {
public static void main(String[] args) {
final String INPUT_ENC = "UTF-8";
final String OUTPUT_ENC = "UTF-8";
try {
BufferedReader r = new BufferedReader(
new InputStreamReader(System.in, INPUT_ENC));
PrintWriter w = new PrintWriter(
new OutputStreamWriter(System.out, OUTPUT_ENC), true);
String s;
while ((s = r.readLine()) != null) {
w.println(Normalizer.normalize(s, Form.NFC));
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
NFC 정규화는 또한 문자열 동일성 검증에도 매우 적합하다. 단, 문자열 비교 시 적절한 로케일로 초기화된 java.text.Collator
클래스를 사용해야 한다는 점에 유의할 것. Collator
클래스를 사용하는 이유는 언어에 따라 악센트 부호가 붙은 글자들의 정렬 순서가 다르기 때문이다. 정렬 방식에 있어서 어떤 언어는 악센트 부호가 붙은 글자를 기본 글자 바로 다음에 배치하고 또 어떤 경우에는 모든 기본 글자 다음에 배치하기도 한다.
예제: NFD
전화번호부(phone directory) 애플리케이션을 개발 중이라고 가정해보자. 이때, 디렉토리 데이터를 데이터베이스에 저장하고 데이터를 찾기 위한 검색 형식을 설정한다. 전세계의 인명에는 악센트 부호가 붙은 문자가 다수 포함되어 있으므로 다음과 같은 두 가지의 문제가 발생하게 된다. 즉, 대부분의 데이터베이스는 악센트 부호가 붙은 문자를 좋아하지 않을 뿐 아니라 대부분의 애플리케이션 사용자는 검색 형식에 정확한(악센트 부호가 붙은) 이름을 입력하는 번거로움을 피하려 하거나 입력 방법 자체를 모르는 경우가 많다. 따라서 데이터베이스에 저장된 데이터와 검색 형식에서 읽어들인 데이터 양쪽에서 악센트 부호를 모두 제거해야만 한다.
다음 코드는 표준 인풋을 한 행씩 읽고 각 행에서 악센트 부호가 붙은 문자를 제거한 다음 결과를 표준 아웃풋에 작성하는 경우이다. 이 때, 인풋과 아웃풋 모두에 UTF-8 인코딩이 사용된다.
import java.io.*;
import java.text.Normalizer;
import java.text.Normalizer.Form;
public class NFD {
public static void main(String[] args) {
final String INPUT_ENC = "UTF-8";
final String OUTPUT_ENC = "UTF-8";
try {
BufferedReader r = new BufferedReader(
new InputStreamReader(System.in, INPUT_ENC));
PrintWriter w = new PrintWriter(
new OutputStreamWriter(System.out, OUTPUT_ENC), true);
String s;
while ((s = r.readLine()) != null) {
// decompose and remove accents
String decomposed = Normalizer.normalize(s, Form.NFD);
String accentsGone =
decomposed.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
w.println(accentsGone);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
예제: NFKC
NFKC 정규화는 호환성 분해 형식을 가진 조합 마크가 있는 문자에 영향을 미친다. 따라서, 문자 시퀀스 U+1E9B U+0323
(LATIN SMALL LETTER LONG S WITH DOT ABOVE
다음에 오는 COMBINING DOT BELOW
)은 단일 문자 값 U+1E69
로 변환된다. 정규화된 문자는 'ṩ'(LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
)이다.
이 정규화 형식은 IDN(International Domain Names)을 위한 문자열 프로파일 스펙(RFC 3491)을 준수하는 데 필요한데, 도메인명에 비 ASCII 문자가 포함되어 있는 경우에는 반드시 NFKC 형식으로 정규화되어야 한다. 따라서 국제 도메인명 등록이 필요한 애플리케이션을 작성하는 경우에는 반드시 도메인명을 이 형식으로 인코딩해야 한다.
IDN 인코딩의 경우 유니코드 버전 3.2를 지정하고 있으며, 유니코드 버전 3.2와 4.0 간에는 일부 CJK 한자 표기를 위한 전처리 형식에 차이가 있다는 점에 유의할 것. RFC 3491을 구현하지 않고 단지 전처리된 도메인명을 얻고자 할 경우에는 java.net.IDN
클래스가 제공하는 기능을 사용하면 된다.
인코딩 과정은 NFC 예제에 표시된 것과 유사하며, 차이점이라면 Form.NFC
대신 Form.NFKC
인코딩 형식을 사용해야 한다는 것뿐이다.
예제: NFKD
이 정규화 형식은 레거시 텍스트 데이터를 XML 포맷으로 변환할 때 특히 유용하다. Unicode in XML and other Markup Languages 스펙의 경우 호환 가능한 문자들을 처리하기 위한 몇 가지 규칙을 정의하고 있는데, 예를 들어 이 스펙은 위 첨자와 아래 첨자에는 <sup>
와 <sub>
마크업을, 분수 표현에는 MathML 마크업을, 그리고 원 숫자 대신 글머리표(list-item marker) 스타일을 사용할 것 등을 권하고 있다. 레거시 데이터를 XML로 변환하는 애플리케이션을 작성 중이라면 NFKD로 정규화된 텍스트 데이터에 적절한 마크업과 스타일을 적용하는 것을 고려해야 한다.
데이터를 NFKD 형식으로 변환하려면 Normalizer.normalize
메소드에 두 번째 매개변수로 Form.NFKD
를 건네주어야 한다.
Normalizer.normalize(s, Form.NFKD);
java.text.Normalizer
클래스는 주어진 문자 시퀀스가 네 가지 정규화 형식 중 하나에 의거하여 정규화되었는지 여부를 확인하는 isNormalized
메소드를 정의한다. 다음 코드는 표준 인풋에서 행을 읽고 네 가지 형식 중 하나로 정규화되었는지 여부를 보고한다. 인풋에는 UTF-8 인코딩이 사용된다.
import java.io.*;
import java.text.Normalizer;
import java.text.Normalizer.Form;
public class IsNormalized {
public static void main(String[] args) {
final String INPUT_ENC = "UTF-8";
final Form[] forms = { Form.NFC, Form.NFD, Form.NFKC, Form.NFKD };
try {
BufferedReader r = new BufferedReader(
new InputStreamReader(System.in, INPUT_ENC));
String s;
int line = 1;
while ((s = r.readLine()) != null) {
System.out.printf("%5d:", line++);
for (Form f : forms) {
if (Normalizer.isNormalized(s, f)) {
System.out.print(" " + f.toString());
}
}
System.out.println();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
관련 상세 정보
다음 문서에 정규화에 관한 자세한 내용이 나와 있다.