초기 Java 버전에서는 16비트 char 데이터 유형을 사용하여 유니코드 문자를 표시했다. 당시에는 모든 유니코드 문자가
65,535(0xFFFF) 미만의 값을 가지고 있었고 16비트로 표현할 수 있었기 때문에 이런 디자인이 통했다.
하지만, 이후로 유니코드가 최대 1,114,111(0x10FFFF)의 값까지 증가했다. 16비트 값은 유니코드 버전 3.1에서 모든 유니코드 문자를 표시하기에 너무 작았기
때문에, UTF-32 인코딩 스킴에 대해 32비트 값(코드 포인트라고 함)이 채택되었다.
그러나 효율적인 메모리 사용을 위해 32비트 값보다는 16비트 값을 선호하므로, 유니코드는 16비트 값을 계속 사용할 수 있도록 허용하는 새로운 디자인을 도입했다. UTF-16 인코딩 스킴에서 채택된 이 디자인에서는 16비트 high surrogate에 1,024개의 값을 지정하고 16비트 low surrogate에도 다른 1,024개의 값을 지정한다. 이 디자인에서는 high surrogate 다음에 low surrogate를 사용하여(서로게이트 쌍) 65,536(0x10000)과 1,114,111(0x10FFFF) 사이에 있는 1,048,576(0x100000)개(즉, 1,024×1,024개)의 값을 표시한다.
Java 1.5에서는 (기존 프로그램과의 호환성을 위해) UTF-16 값을 표시하려고 char 유형의 작동을 그대로 유지했고, UTF-32 값을
표시하기 위해 코드 포인트의 개념을 구현했다. (JSR 204: 유니코드 보조 문자 지원에 따라 구현된) 이 확장에서는 유니코드 코드 포인트의 정확한 값이나 변환
알고리즘을 기억할 필요가 없지만, 서로게이트 API의 올바른 사용법을 이해하는 것이 중요하다.
동아시아 국가 및 지역에서는 사용자의 요구에 부응하여 최근 몇 년 사이에 문자 세트 표준의 문자 수를 크게 늘렸다. 이런 표준에 중국 국가 표준 조직의 GB 18030과
일본 국가 표준 조직의 JIS X 0213이 포함된다.
따라서 프로그램들이 유니코드 서로게이트 쌍을 지원하기 위해 이런 표준을 준수할 필요성이 점점 커지고 있다. 이 기사에서는 소프트웨어를
char 유형의 문자만 지원하는 버전에서 서로게이트 쌍을 처리할 수 있는 새 버전으로 리디자인할 계획을 가진 독자들을 위해
관련 Java API 및 코딩 옵션을 설명한다. 또한, 서로게이트 쌍을 위한 지원을 추가하면 처리 시간에 어떤 영향을 미치는지도 평가해본다.
순차 액세스는 Java 언어로 문자열을 처리하기 위한 기본 작업 중 하나다. 이 접근 방식에서는 앞에서 뒤로 또는 때때로 뒤에서 앞으로 순차적으로 입력 문자열의 각 문자에 액세스한다. 이 섹션에서는 순차 액세스를 사용하여 문자열에서 32비트 코드 포인트 배열을 작성하기 위한 기법을 설명한 7개의 예제를 살펴보고, 그 처리 시간을 평가한다.
예제 1-1: 벤치마크(서로게이트 쌍에 대한 지원 없음)
목록 1에서는 서로게이트 쌍에 관심을 두지 않고 32비트 코드 포인트 값에 16비트 char 유형의 값을 직접 지정한다.
목록 1. 서로게이트 쌍에 대한 지원 없음
int[] toCodePointArray(String str) { // Example 1-1
int len = str.length(); // the length of str
int[] acp = new int[len]; // an array of code points
for (int i = 0, j = 0; i < len; i++) {
acp[j++] = str.charAt(i);
}
return acp;
}
|
이 예제에서는 서로게이트 쌍을 지원하지 않지만, 이후의 순차 액세스 예제와 비교하기 위한 처리 시간 벤치마크를 제공한다.
목록 2에서는 isSurrogatePair()를 사용하여 서로게이트 쌍의 총 개수를 계산한다. 계산이 끝나면 코드 포인트 배열에 적당한 메모리를
할당하여 값을 저장한다. 다음으로, 순차 액세스 루프로 들어가서 isHighSurrogate()와 isLowSurrogate()를
사용하여 각 서로게이트 문자가 high surrogate인지, low surrogate인지 결정한다. High surrogate 다음에 low surrogate가 나오는 것으로 밝혀지면,
toCodePoint()를 사용하여 서로게이트 쌍을 코드 포인트 값으로 변환하고 현재 인덱스를 2씩 증가시킨다. 그렇지 않으면,
char 유형의 값을 직접 코드 포인트 값으로 지정하여 현재 인덱스를 1씩 증가시킨다. 처리 시간은 예제 1-1에서의
처리 시간보다 1.38배 더 길다.
목록 2. 제한적 지원
int[] toCodePointArray(String str) { // Example 1-2
int len = str.length(); // the length of str
int[] acp; // an array of code points
int surrogatePairCount = 0; // the count of surrogate pairs
for (int i = 1; i < len; i++) {
if (Character.isSurrogatePair(str.charAt(i - 1), str.charAt(i))) {
surrogatePairCount++;
i++;
}
}
acp = new int[len - surrogatePairCount];
for (int i = 0, j = 0; i < len; i++) {
char ch0 = str.charAt(i); // the current char
if (Character.isHighSurrogate(ch0) && i + 1 < len) {
char ch1 = str.charAt(i + 1); // the next char
if (Character.isLowSurrogate(ch1)) {
acp[j++] = Character.toCodePoint(ch0, ch1);
i++;
continue;
}
}
acp[j++] = ch0;
}
return acp;
}
|
목록 2와 같이 순진한 방법으로 소프트웨어를 업데이트하는 것은 문제가 있다. 이 방법은 지루한데다 광범위한 수정 작업도 필요하므로, 최종 소프트웨어가 취약하고 나중에 변경하기 어려워진다. 특히, 다음과 같은 문제점이 있다.
- 충분한 메모리 할당을 위해 코드 포인트 수를 계산할 필요성
- 문자열에서 지정된 인덱스에 대해 올바른 코드 포인트 값을 얻기 어려움
- 다음 처리 단계를 위해 현재 인덱스를 올바로 이동하기 어려움
다음 예제에서 개선된 알고리즘이 표시된다.
Java 1.5에서는 예제 1-2의 세 가지 문제점을 각각 해결하기 위해 codePointCount(),
codePointAt() 및 offsetByCodePoints() 메소드를 제공했다. 목록 3에서는 이들 메소드를 사용하여
알고리즘의 가독성을 개선한다.
목록 3. 기본 지원
int[] toCodePointArray(String str) { // Example 1-3
int len = str.length(); // the length of str
int[] acp = new int[str.codePointCount(0, len)];
for (int i = 0, j = 0; i < len; i = str.offsetByCodePoints(i, 1)) {
acp[j++] = str.codePointAt(i);
}
return acp;
}
|
하지만, 목록 3에 대한 처리 시간은 목록 1에 비해 2.80배 더 길다.
offsetByCodePoints()에서 음수를 두 번째 매개변수로 받을 때, 문자열 헤드 쪽으로 절대 오프셋을 계산할 수 있다. 다음으로,
codePointBefore()는 지정된 인덱스 직전에 코드 포인트 값을 리턴할 수 있다. 목록 4에서 이런 메소드를 사용하여 뒤에서 앞으로
문자열을 순회한다.
목록 4.
codePointBefore()를 이용한 기본 지원
int[] toCodePointArray(String str) { // Example 1-4
int len = str.length(); // the length of str
int[] acp = new int[str.codePointCount(0, len)];
int j = acp.length; // an index for acp
for (int i = len; i > 0; i = str.offsetByCodePoints(i, -1)) {
acp[--j] = str.codePointBefore(i);
}
return acp;
}
|
이 예제에서의 처리 시간(예제 1-1에 비해 2.72배의 시간이 걸림)은 예제 1-3보다 다소 빠르다. 일반적으로, 0이 아닌 값보다는 0과 비교할 때 JVM의 코드 크기가 더 작고, 이렇게 하면 가끔 성능이 개선된다. 하지만, 이런 작은 개선이 가독성 손실보다 가치가 없을 수도 있다.
예제 1-3과 1-4에서는 서로게이트 쌍을 위한 기본 지원을 제공한다. 이 두 예제는 임시 변수가 필요 없는 강력한 코딩
방식이다. 처리 시간 단축을 위해 offsetByCodePoints() 대신 charCount()를 사용하는 것이 효과적이지만,
목록 5에 표시된 것처럼 임시 변수가 코드 포인트 값을 보유하고 있어야 한다.
목록 5.
charCount()를 이용해 최적화된 지원
int[] toCodePointArray(String str) { // Example 1-5
int len = str.length(); // the length of str
int[] acp = new int[str.codePointCount(0, len)];
int j = 0; // an index for acp
for (int i = 0, cp; i < len; i += Character.charCount(cp)) {
cp = str.codePointAt(i);
acp[j++] = cp;
}
return acp;
}
|
목록 5에 대한 처리 시간이 예제 1-1에 비해 1.68배로 단축된다.
목록 6에서는 예제 1-5에 표시된 최적화를 사용하는 동안 char 유형의 배열에 직접 액세스한다.
목록 6.
char 배열을 이용해 최적화된 지원
int[] toCodePointArray(String str) { // Example 1-6
char[] ach = str.toCharArray(); // a char array copied from str
int len = ach.length; // the length of ach
int[] acp = new int[Character.codePointCount(ach, 0, len)];
int j = 0; // an index for acp
for (int i = 0, cp; i < len; i += Character.charCount(cp)) {
cp = Character.codePointAt(ach, i);
acp[j++] = cp;
}
return acp;
}
|
char 배열은 toCharArray()를 사용하여 문자열에서 복사된다. 배열에 직접 액세스하는 것이
메소드를 통한 간접 액세스보다 빠르기 때문에 성능이 개선된다. 처리 시간은 예제 1-1의 처리 시간보다 1.51배 길다. 하지만,
toCharArray()를 호출할 때 이 배열에 새 어레이를 작성하고 데이터를 이 배열로 복사하는 데 약간의 오버헤드가 있다. String
클래스에서 제공하는 편리한 메소드를 사용할 수 없다. 그래도 이 알고리즘은 대량의 데이터를 처리하는 데 유용하다.
목록 7에 표시된 것처럼, 이 예제의 오브젝트 지향 알고리즘에서는 CharBuffer 클래스를 사용한다.
목록 7.
CharSequence를 이용한 오브젝트 지향 접근 방식
int[] toCodePointArray(String str) { // Example 1-7
CharBuffer cBuf = CharBuffer.wrap(str); // Buffer to wrap str
IntBuffer iBuf = IntBuffer.allocate( // Buffer to store code points
Character.codePointCount(cBuf, 0, cBuf.capacity()));
while (cBuf.remaining() > 0) {
int cp = Character.codePointAt(cBuf, 0); // the current code point
iBuf.put(cp);
cBuf.position(cBuf.position() + Character.charCount(cp));
}
return iBuf.array();
}
|
이전 예제들과는 달리, 목록 7에서는 순차 액세스를 위한 현재 위치를 유지하기 위해 인덱스를 사용할 필요가 없다. 그 대신, CharBuffer가
현재 위치를 내부적으로 추적한다. Character 클래스는 CharSequence 인터페이스를 통해
CharBuffer를 처리할 수 있는 정적 메소드인 codePointCount()와 codePointAt()을
제공한다. CharBuffer는 항상 현재 위치를 CharSequence의 첫 부분으로 설정한다. 따라서
codePointAt()이 호출될 때 두 번째 매개변수가 항상 0으로 설정된다. 처리 시간은 예제 1-1에서의
처리 시간보다 2.15배 더 길다.
순차 액세스 예제에 대한 타이밍 테스트에서는 10,000개의 서로게이트 쌍과 서로게이트가 아닌 것 10,000개를 포함한 샘플 문자열을 사용했다. 코드 포인트 배열은 문자열로부터 10,000회 작성되었으며, 테스트 환경은 다음으로 구성되었다.
- OS: Microsoft Windows® XP Professional SP2
- Java: IBM Java 1.5 SR7
- CPU: Intel® Core 2 Duo CPU T8300 @ 2.40GHz
- 메모리: 2.97GB RAM
표 1은 예제 1-1에서 1-7까지의 절대 및 상대 처리 시간과 연관된 API를 나타낸 것이다.
표 1. 순차 액세스 예제에 대한 처리 시간과 API
| 예제 | 설명 | 처리 시간 (ms) | 예제 1-1에 대한 비율 | API |
|---|---|---|---|---|
| 1-1 | 서로게이트 쌍에 대한 지원 없음 | 2031 | 1.00 | |
| 1-2 | 제한적 지원 | 2797 | 1.38 | Character 클래스:
|
| 1-3 | 기본 지원 | 5687 | 2.80 | String 클래스:
|
| 1-4 | codePointBefore()를 이용한 기본 지원 | 5516 | 2.72 | String 클래스:
|
| 1-5 | charCount()를 이용해 최적화된 지원 | 3406 | 1.68 | Character 클래스:
|
| 1-6 | char 배열을 이용해 최적화된 지원 | 3062 | 1.51 | Character 클래스:
|
| 1-7 | CharSequence를 이용한 오브젝트 지향 접근 방식 | 4360 | 2.15 | Character 클래스:
|
임의 액세스는 문자열에서 임의의 위치에 대한 직접 액세스이다. 문자열에 액세스할 때, 인덱스 값은 16비트 char 유형의 단위를
기준으로 한다. 하지만, 문자열에서 32비트 코드 포인트를 사용하는 경우에는 32비트 코드 포인트의 단위를 기반으로 한 인덱스를 사용하여 액세스할 수
없다. offsetByCodePoints()를 사용하여 코드 포인트에 대한 인덱스를 char 유형에 대한 인덱스로 변환해야
한다. offsetByCodePoints()는 항상 두 번째 매개변수를 사용하여 첫 번째 매개변수에서 문자열의 내부에 대한 계산을 하기 때문에,
이는 알고리즘이 올바로 디자인되어 있지 않은 경우 성능 저하를 초래할 수 있다. 이 섹션에서는 짧은 단위를 사용하여 긴 문자열을 분리하는 세 가지 예제를
비교해본다.
예제 2-1: 벤치마크(서로게이트 쌍에 대한 지원 없음)
목록 8은 너비 단위로 문자열을 분리하는 방법을 나타낸 것이다. 이것은 서로게이트 쌍을 지원하지 않는 이후의 예제들을 위한 벤치마크이다.
목록 8. 서로게이트 쌍에 대한 지원 없음
String[] sliceString(String str, int width) { // Example 2-1
// It must be that "str != null && width > 0".
List<String> slices = new ArrayList<String>();
int len = str.length(); // (1) the length of str
int sliceLimit = len - width; // (2) Do not slice beyond here.
int pos = 0; // the current position per char type
while (pos < sliceLimit) {
int begin = pos; // (3)
int end = pos + width; // (4)
slices.add(str.substring(begin, end));
pos += width; // (5)
}
slices.add(str.substring(pos)); // (6)
return slices.toArray(new String[slices.size()]); }
|
sliceLimit 변수는 문자열의 나머지가 현재 너비 단위로 분리할 수 있을 만큼 충분히 길지 않을 때 IndexOutOfBoundsException의
인스턴스를 발생시키지 않기 위해 분리 위치의 한계를 유지한다. 이 알고리즘은 현재 위치가 sliceLimit를 초과할 때
while 루프에서 이스케이프한 후 마지막 조각을 처리한다.
목록 9는 코드 포인트의 인덱스를 사용하여 문자열에 임의로 액세스하는 방법을 나타낸 것이다.
목록 9. 성능 저하
String[] sliceString(String str, int width) { // Example 2-2
// It must be that "str != null && width > 0".
List<String> slices = new ArrayList<String>();
int len = str.codePointCount(0, str.length()); // (1) code point count [Modified]
int sliceLimit = len - width; // (2) Do not slice beyond here.
int pos = 0; // the current position per code point
while (pos < sliceLimit) {
int begin = str.offsetByCodePoints(0, pos); // (3) [Modified]
int end = str.offsetByCodePoints(0, pos + width); // (4) [Modified]
slices.add(str.substring(begin, end));
pos += width; // (5)
}
slices.add(str.substring(str.offsetByCodePoints(0, pos))); // (6) [Modified]
return slices.toArray(new String[slices.size()]); }
|
목록 9는 목록 8에서 여러 행을 변경한다. 우선, (1)행에서 length()를
codePointCount()로 바꾼다. 그 다음, offsetByCodePoints()를 사용하여
char 유형의 인덱스를 (3), (4), (6)행에 있는 코드 포인트의 인덱스로 바꾼다.
이 알고리즘의 기본적인 플로우는 예제 2-1과 거의 같아 보인다. 그러나 offsetByCodePoints()는 항상
앞에서 지정된 인덱스까지 문자열의 내부를 계산하기 때문에, 예제 2-1에 비해 문자열 길이에 비례하여 처리 시간이 늘어난다.
목록 10에 표시된 접근 방식을 이용해 예제 2-2의 성능 문제를 피할 수 있다.
목록 10. 성능 개선
String[] sliceString(String str, int width) { // Example 2-3
// It must be that "str != null && width > 0".
List<String> slices = new ArrayList<String>();
int len = str.length(); // (1) the length of str
int sliceLimit // (2) Do not slice beyond here. [Modified]
= (len >= width * 2 || str.codePointCount(0, len) > width)
? str.offsetByCodePoints(len, -width) : 0;
int pos = 0; // the current position per char type
while (pos < sliceLimit) {
int begin = pos; // (3)
int end = str.offsetByCodePoints(pos, width); // (4) [Modified]
slices.add(str.substring(begin, end));
pos = end; // (5) [Modified]
}
slices.add(str.substring(pos)); // (6)
return slices.toArray(new String[slices.size()]); }
|
우선, (목록 9의) len-width 표현식이 (2)행에서 offsetByCodePoints(len,-width)로
바뀐다. 하지만, 이로 인해 width의 값이 코드 포인트 수보다 클 때 IndexOutOfBoundsException의
인스턴스가 발생한다. try/catch 예외 핸들러를 포함한 절이 다른 해결책이 되겠지만, 예외를 피하기 위한
경계 조건을 고려해야 한다. 모든 코드 포인트가 서로게이트 쌍으로 변환되더라도 코드 포인트 수가 width의 값을 초과하기 때문에,
len>width*2 표현식이 참인 경우 offsetByCodePoints()를 안전하게 호출할 수 있다. 또는
codePointCount(0,len)>width 표현식이 참인 경우 offsetByCodePoints()를 안전하게 호출할
수도 있다. 다른 경우에는 sliceLimit를 0으로 설정해야 한다.
while 루프의 (4)행에서 목록 9에 있는 pos + width 표현식을
offsetByCodePoints(pos,width)로 바꿔야 한다. 첫 번째 매개변수는 현재 위치를, 두 번째 매개변수는 width의
값을 지정하기 때문에, 필요한 계산의 양은 width 값 이내의 범위이다. 다음으로, (5)행에서 pos+=width
표현식을 pos=end 표현식으로 바꿔야 한다. 이렇게 하면 같은 인덱스를 계산하기 위해 offsetByCodePoints()를
두 번 호출하지 않아도 된다. 소스 코드를 더 많이 수정하여 처리 시간을 더 줄일 수 있다.
그림 1과 2는 예제 2-1, 2-2 및 2-3의 처리 시간을 나타낸 것이다. 샘플 문자열에는 같은 수의
서로게이트 쌍과 비 서로게이트 쌍이 들어 있었다. 문자열 길이와 width의 값이 변경되는 동안 샘플 문자열은 10,000회 분리되었다.
그림 1. 조각의 일정한 너비
그림 2. 조각의 일정한 수
예제 2-1과 2-3에서는 길이에 비례하여 처리 시간이 증가하는 반면, 예제 2-2에서는 길이의 제곱에
비례하여 처리 시간이 증가한다. 조각의 수가 고정되어 있는 상태에서 문자열의 길이와 width의 값이 증가할 때, 예제 2-1에서는
처리 시간이 일정한 데 반해, 예제 2-2와 2-3에서는 width의 값에 비례하여 처리 시간이
증가한다.
서로게이트로 작업하기 위한 대부분의 정보 API에는 이름이 같은 두 가지 유형의 메소드가 있다. 하나는 16비트 char 유형으로
매개변수를 받고, 다른 하나는 32비트 코드 포인트로 매개변수를 받는다. 표 2는 각 API의 리턴 값을 나타낸 것이다. U+20B9F가 U+D842 다음에 U+DF9F가 나오는
서로게이트 쌍으로 변환될 때, 세 번째 열은 U+53F1, 네 번째 열은 U+20B9F, 마지막 열은 U+D842(high surrogate)에 대한 것이다. U+20B9F 대신 U+D842의 값을
사용하면 프로그램에서 서로게이트 쌍을 처리할 수 없는 경우 (표 2에 굵은 기울임꼴로 나타낸) 예상치 못한 결과를 초래한다.
표 2. 서로게이트용 정보 API
| 클래스 | 메소드/생성자 | U+53F1에 대한 값 | U+20B9F에 대한 값 | U+D842에 대한 값 |
|---|---|---|---|---|
Character | static byte getDirectionality(int cp) | 0 | 0 | 0 |
static int getNumericValue(int cp) | -1 | -1 | -1 | |
static int getType(int cp) | 5 | 5 | 19 | |
static boolean isDefined(int cp) | true | true | true | |
static boolean isDigit(int cp) | false | false | false | |
static boolean isISOControl(int cp) | false | false | false | |
static boolean isIdentifierIgnorable(int cp) | false | false | false | |
static boolean isJavaIdentifierPart(int cp) | true | true | false | |
static boolean isJavaIdentifierStart(int cp) | true | true | false | |
static boolean isLetter(int cp) | true | true | false | |
static boolean isLetterOrDigit(int cp) | true | true | false | |
static boolean isLowerCase(int cp) | false | false | false | |
static boolean isMirrored(int cp) | false | false | false | |
static boolean isSpaceChar(int cp) | false | false | false | |
static boolean isSupplementaryCodePoint(int cp) | false | true | false | |
static boolean isTitleCase(int cp) | false | false | false | |
static boolean isUnicodeIdentifierPart(int cp) | true | true | false | |
static boolean isUnicodeIdentifierStart(int cp) | true | true | false | |
static boolean isUpperCase(int cp) | false | false | false | |
static boolean isValidCodePoint(int cp) | true | true | true | |
static boolean isWhitespace(int cp) | false | false | false | |
static int toLowerCase(int cp) | (변경 불가능) | |||
static int toTitleCase(int cp) | (변경 불가능) | |||
static int toUpperCase(int cp) | (변경 불가능) | |||
Character. | Character.UnicodeBlock of(int cp) | CJK_UNIFIED_IDEOGRAPHS | CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B | HIGH_SURROGATES |
Font | boolean canDisplay(int cp) | (Font 인스턴스에 따라 다름) | ||
FontMetrics | int charWidth(int cp) | (FontMetrics 인스턴스에 따라 다름) | ||
문자열 | int indexOf(int cp) | (String 인스턴스에 따라 다름) | ||
int lastIndexOf(int cp) | (String 인스턴스에 따라 다름) | |||
이 섹션에서는 이전 섹션에서 설명하지 않은 서로게이트 쌍 관련 API를 다룬다. 표 3은 이런 나머지 API를 전부 나타낸 것이다. 표 1, 2 또는 3에 모든 서로게이트 쌍 API가 표시되어 있다.
표 3. 기타 서로게이트 API
| 클래스 | 메소드/생성자 |
|---|---|
Character | static int codePointAt(char[] ach, int index, int limit) |
static int codePointBefore(char[] ach, int index) | |
static int codePointBefore(char[] ach, int index, int start) | |
static int codePointBefore(CharSequence seq, int index) | |
static int digit(int cp, int radix) | |
static int offsetByCodePoints(char[] ach, int start, int count, int index, int cpOffset) | |
static int offsetByCodePoints(CharSequence seq, int index, int cpOffset) | |
static char[] toChars(int cp) | |
static int toChars(int cp, char[] dst, int dstIndex) | |
문자열 | String(int[] acp, int offset, int count) |
int indexOf(int cp, int fromIndex) | |
int lastIndexOf(int cp, int fromIndex) | |
StringBuffer | StringBuffer appendCodePoint(int cp) |
int codePointAt(int index) | |
int codePointBefore(int index) | |
int codePointCount(int beginIndex, int endIndex) | |
int offsetByCodePoints(int index, int cpOffset) | |
StringBuilder | StringBuilder appendCodePoint(int cp) |
int codePointAt(int index) | |
int codePointBefore(int index) | |
int codePointCount(int beginIndex, int endIndex) | |
int offsetByCodePoints(int index, int cpOffset) | |
IllegalFormat | IllegalFormatCodePointException(int cp) |
int getCodePoint() |
목록 11은 코드 포인트에서 문자열을 작성하는 다섯 가지 방법을 나타낸 것이다. 테스트에 사용된 코드 포인트는 한 문자열에서 수십 억 번 반복된 U+53F1과 U+20B9F였다. 목록 11에서 처리 시간은 설명으로 표시된다.
목록 11. 코드 포인트에서 문자열을 작성하는 다섯 가지 방법
int cp = 0x20b9f; // CJK Ideograph Extension B
String str1 = new String(new int[]{cp}, 0, 1); // processing time: 206ms
String str2 = new String(Character.toChars(cp)); // 187ms
String str3 = String.valueOf(Character.toChars(cp)); // 195ms
String str4 = new StringBuilder().appendCodePoint(cp).toString(); // 269ms
String str5 = String.format("%c", cp); // 3781ms
|
str1, str2, str3 및 str4에 대한
처리 시간이 현저히 다르지는 않다. 이들과는 달리, str5에서는 로케일 및 형식 정보를 바탕으로 유연한 출력을 지원하는
String.format()을 사용하기 때문에 작성 시간이 훨씬 더 오래 걸린다. str5 접근 방식은 프로그램의
끝 부분 근처에서 텍스트를 출력할 때만 사용해야 한다.
유니코드의 모든 새 버전에서는 서로게이트 쌍으로 표시되는 새로 정의된 문자를 얻는다. 각각의 아시아 문자 세트 표준이 그런 문자의 유일한 소스는 아니다. 예를 들어, 다양한 고대 문자와 같이 휴대폰에서 이모티콘 지원도 요구된다. 이 기사에서 알게 된 기술과 성능 분석 기법은 Java 애플리케이션에서 이들을 모두 효율적으로 지원하는 데 도움이 될 것이다.
교육
-
Unicode Consortium: 이 비영리 조직에서는 현대적 소프트웨어 제품 및 표준에서 텍스트 표시를 지정하는 유니코드 표준을
개발 및 확장하고 이 표준의 사용을 장려한다.
-
"Supplementary Characters in the Java Platform"(Norbert
Lindenberg 및 Masayoshi Okutsu, java.sun.com, 2004년 5월): 유니코드 보조 문자가 Java 플랫폼에서 어떻게 지원되는지 자세히 알아보자.
-
JDK 5.0 documentation: 유니코드 서로게이트 API에 대한 공식 Javadoc를 탐색할 수 있다.
-
JSR204: Unicode Supplementary Character Support: Java 1.5에 도입된 유니코드 API는 Java 스펙 요청에서 비롯되었다.
-
International Components for Unicode (ICU): ICU는 소프트웨어 애플리케이션을 위한
유니코드 및 세계화 지원을 제공하는 라이브러리 세트이다. ICU는 IBM에서 후원, 지원 및 사용하는 오픈 소스 개발 프로젝트이다.
-
developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자.
제품 및 기술 얻기
-
자신에게 가장한 적합한 방법으로 IBM
제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나,
SOA Sandbox에서
SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.
토론
- My developerWorks 커뮤니티에 참여하자.
개발자가 이끌고 있는 블로그, 포럼, 그룹 및 Wiki를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.