> Java > java지도 시간 > 본문

Java의 코드 포인트 및 UTF-16과 관련된 몇 가지 사항에 대한 자세한 소개

黄舟
풀어 주다: 2017-03-21 11:11:40
원래의
1523명이 탐색했습니다.

유니코드와 UTF-8/UTF-16/UTF-32의 관계

유니코드와 UTF-8/UTF-16/UTF-32의 관계는 문자 집합 및 인코딩 관계. 문자 집합의 개념에는 실제로 두 가지 측면이 포함됩니다. 하나는 문자 집합이고 다른 하나는 인코딩 체계입니다. 문자 세트는 포함된 모든 기호를 정의합니다. 좁은 의미의 문자 세트는 인코딩 체계를 포함하지 않고 이 문자 세트에 속하는 모든 기호를 정의합니다. 그러나 일반적으로 문자 집합은 문자 모음을 정의할 뿐만 아니라 각 기호에 대한 이진 인코딩도 정의합니다. GB2312 또는 ASCII를 언급하면 ​​인코딩 체계가 GB2312 또는 ASCII임을 암시적으로 나타냅니다. 이러한 경우 문자 집합과 인코딩 체계는 동일한 것으로 간주될 수 있습니다.

하지만 유니코드에는 여러 인코딩 체계가 있습니다. 유니코드 문자 집합으로 지정된 표준 인코딩 체계는 UCS-2(UTF-16)로, 유니코드 문자를 표현하기 위해 2바이트를 사용합니다(UTF-16의 2바이트는 기본 다국어 플랫 문자, 4바이트). 보조 평면 문자입니다). UCS-4(UTF-32)는 4바이트를 사용하여 유니코드 문자를 나타냅니다. 일반적으로 사용되는 또 다른 유니코드 인코딩 방식인 UTF-8은 1~4개의 가변 길이 바이트를 사용하여 유니코드 문자를 나타내며 간단한 변환 알고리즘을 통해 UTF-16에서 직접 가져올 수 있습니다. 따라서 유니코드 문자 집합을 사용할 때 적절한 시나리오에 사용되는 여러 인코딩 체계가 있습니다.

더 간단하게 말하면 유니코드 문자 집합은 모든 문자(예: 이미지)와 해당 유니코드 코드(특정 인코딩 체계에 관계없이), UTF-8/UTF-를 기록하는 사전과 동일합니다. 16/UTF-32 코드는 유니코드 코드를 해당 수식을 통해 계산하여 실제로 저장 및 전송되는 데이터입니다.

UTF-16

JVM 사양에는 Java의 char 유형에서 사용하는 인코딩 방식이 UTF-16이라고 명시되어 있으므로 먼저 UTF-16에 대해 이해해 봅시다.

유니코드의 인코딩 공간은 U+0000에서 U+10FFFF까지입니다. 문자를 매핑하는 데 사용할 수 있는 코드 포인트는 1112064개입니다. 코딩 공간의 이 부분은 17개의 평면으로 나눌 수 있으며, 각 평면은 2^16(65536) 코드 비트를 포함합니다. 첫 번째 평면은 BMP(Basic Multilingual Plane) 또는 평면 0이라고 합니다. 다른 평면을 보조 평면이라고 합니다. 기본 다국어 평면에서 U+D800부터 U+DFFF까지의 코드 포인트 블록은 영구적으로 예약되어 있으며 유니코드 문자에 매핑되지 않습니다. UTF-16은 0xD800-0xDFFF 섹션의 예약된 코드 비트를 사용하여 보조 평면 문자의 코드 비트를 인코딩합니다.

가장 많이 사용되는 문자는 BMP에 포함되며 2바이트로 표현됩니다. 보조 평면의 코드 비트는 대리 쌍이라고 하는 UTF-16의 16비트 긴 코드 요소 쌍으로 인코딩됩니다. 구체적인 방법은 다음과 같습니다.

  • 0×10000 빼기 코드 비트에서 결과 값의 범위는 0에서 0xFFFFF까지이며 길이는 20비트입니다.

  • 0xD800에 상위 10비트 값(값 범위는 0~0x3FF)을 더해 첫 번째 코드 요소를 얻거나 상위 서로게이트(high surrogate)라고 부르는데, 값 범위는 0xD800~이다. 0xDBFF. 상위 프록시의 값이 하위 프록시의 값보다 작으므로 혼동을 피하기 위해 유니코드 표준에서는 이제 상위 프록시 리드 대체를 호출합니다.

  • 하위 10비트 값(값 범위도 0~0x3FF)을 0xDC00에 추가하여 두 번째 코드 요소를 가져오거나 현재 값을 하위 서로게이트라고 합니다. 하위 서로게이트는 상위 서로게이트보다 더 큰 값을 가지므로 혼동을 피하기 위해 유니코드 표준에서는 이제 하위 서로게이트 트레일 서로게이트를 호출합니다.

예를 들어 U+10437 인코딩:

  • 0×10437에서 0×10000을 뺀 결과는 0×00437이며 이는 이진수입니다. 0000 0000 0100 0011 0111입니다.

  • 상위 10비트 값과 하위 10비트 값(바이너리 사용)을 0000000001 및 0000110111로 분할합니다.

  • 상위 값에 0xD800을 추가하여 상위 비트를 형성합니다: 0xD800 + 0×0001 = 0xD801.

  • 낮은 값에 0xDC00을 추가하여 하위 비트를 형성합니다: 0xDC00 + 0×0037 = 0xDC37.

BMP의 선행 에이전트, 후행 에이전트 및 유효한 문자의 코드 포인트는 서로 겹치지 않으므로 한 문자 인코딩의 일부가 동일할 수 없습니다. 검색 중 다른 문자 인코딩으로 다른 부분이 겹칩니다. 따라서 하나의 코드 요소(코드 포인트를 구성하는 기본 단위, 2바이트)만 검사하여 특정 문자에 대한 다음 문자의 시작 코드 요소를 결정할 수 있습니다.

Java 관련 코드포인트

문자열 객체의 경우 내용은 char 배열을 통해 저장됩니다. char 유형은 2바이트에 저장되며, 이 2바이트는 실제로 UTF-16 인코딩으로 코드 요소를 저장합니다. charAt 및 length 메소드를 사용할 때 실제로 반환되는 것은 코드 요소와 코드 요소의 수입니다. 일반적으로 문제는 없지만 문자가 보조 평면 문자인 경우 위의 두 메소드는 올바른 결과를 얻지 못합니다. . 올바른 처리 방법은 다음과 같습니다.

int character = aString.codePointAt(i);
int length = aString.codePointCount(0, aString.length());
로그인 후 복사

codePointAt의 반환 값은 char 대신 int입니다. 이 값은 유니코드 코드입니다.

codePointAt 메서드는 codePointAtImpl을 호출합니다:

static int codePointAtImpl(char[] a, int index, int limit) {
        char c1 = a[index];
        if (isHighSurrogate(c1) && ++index < limit) {
            char c2 = a[index];
            if (isLowSurrogate(c2)) {
                return toCodePoint(c1, c2);
            }
        }
        return c1;
    }
로그인 후 복사

isHighSurrogate 메서드는 아래 첨자 문자의 2바이트가 UTF-16(0xD800~0xDBFF)의 선행 서로게이트인지 여부를 결정합니다.

public static boolean isHighSurrogate(char ch) {
        // Help VM constant-fold; MAX_HIGH_SURROGATE + 1 == MIN_LOW_SURROGATE
        return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
    }
로그인 후 복사
public static final char MIN_HIGH_SURROGATE = &#39;\uD800&#39;;
public static final char MAX_HIGH_SURROGATE = &#39;\uDBFF&#39;;
로그인 후 복사

然后++index,isLowSurrogate方法判断下一个字符的2个字节是否为后尾代理(0xDC00~0xDFFF):

public static boolean isLowSurrogate(char ch) {
        return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1);
    }
로그인 후 복사
public static final char MIN_LOW_SURROGATE  = &#39;\uDC00&#39;;
public static final char MAX_LOW_SURROGATE  = &#39;\uDFFF&#39;;
로그인 후 복사

toCodePoint方法将这2个码元组装成一个Unicode码:

public static int toCodePoint(char high, char low) {
        // Optimized form of:
        // return ((high - MIN_HIGH_SURROGATE) << 10)
        //         + (low - MIN_LOW_SURROGATE)
        //         + MIN_SUPPLEMENTARY_CODE_POINT;
        return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
                                       - (MIN_HIGH_SURROGATE << 10)
                                       - MIN_LOW_SURROGATE);
    }
로그인 후 복사

这个过程就是以上将一个辅助平面的Unicode码位转换成2个码元的逆过程。

所以,枚举字符串的正确方法:

for (int i = 0; i < aString.length();) {
	int character = aString.codePointAt(i);
	//如果是辅助平面字符,则i+2
	if (Character.isSupplementaryCodePoint(character)) i += 2;
	else ++i;
}
로그인 후 복사

将codePoint转换为char[]可调用Character.toChars方法,然后可进一步转换为字符串:

new String(Character.toChars(codePoint));
로그인 후 복사

toChars方法所做的就是以上将Unicode码位转换为2个码元的过程。

위 내용은 Java의 코드 포인트 및 UTF-16과 관련된 몇 가지 사항에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿