ホームページ  >  記事  >  Java  >  Java のコードポイントと UTF-16 に関連する事項の詳細な紹介

Java のコードポイントと UTF-16 に関連する事項の詳細な紹介

黄舟
黄舟オリジナル
2017-03-21 11:11:401479ブラウズ

UnicodeとUTF-8/UTF-16/UTF-32の関係

UnicodeとUTF-8/UTF-16/UTF-32の関係は、文字セットとエンコーディングの関係です。文字セットの概念には、実際には 2 つの側面が含まれています。1 つは文字のセットであり、もう 1 つはエンコード スキームです。キャラクタ セットは、それに含まれるすべてのシンボルを定義します。狭義のキャラクタ セットには、このキャラクタ セットに属するすべてのシンボルが定義されるだけです。しかし、一般的に言えば、文字セットは文字のコレクションを定義するだけでなく、各シンボルのバイナリ エンコーディングも定義します。 GB2312 または ASCII について言及する場合、それはエンコード スキームが GB2312 または ASCII であることを暗黙的に示しており、この場合、文字セットとエンコード スキームは等しいと見なされます。

しかし、Unicode には複数のエンコード スキームがあります。 Unicode 文字セットで指定されている標準のエンコード スキームは UCS-2 (UTF-16) であり、Unicode 文字を表すために 2 バイトを使用します (UTF-16 の 2 バイトは基本的な多言語フラット文字であり、4 バイトは補助文字です)平らな文字)。 UCS-4 (UTF-32) は、Unicode 文字を表すために 4 バイトを使用します。もう 1 つの一般的に使用される Unicode エンコード スキーム - UTF-8 は、1 ~ 4 の可変長バイトを使用して Unicode 文字を表し、単純な変換アルゴリズムにより UTF-16 から直接取得できます。したがって、Unicode 文字セットを使用する場合は複数のエンコード スキームがあり、適切なシナリオで使用されます。

より簡単に言うと、Unicode 文字セットは辞書に相当し、すべての文字 (つまり、画像) とそれに対応する Unicode コード (特定のエンコード スキームに関係なく)、UTF-8/UTF-16/UTF- を記録します。 32 コードは、Unicode コードによって対応する計算式を介して計算され、実際に保存および送信されるデータです。

UTF-16

Javaのchar型で使用されるエンコードスキームはUTF-16であるとJVM仕様に明記されているので、まずはUTF-16について理解しましょう。

Unicode のエンコード空間の範囲は U+0000 から U+10FFFF で、文字のマッピングに使用できるコード ポイントは 1112064 です。コーディング空間のこの部分は 17 のプレーンに分割でき、各プレーンには 2^16 (65536) コード ビットが含まれます。最初のプレーンは、Basic Multilingual Plane (BMP)、またはプレーン 0 と呼ばれます。他のプレーンは補助プレーンと呼ばれます。基本的な多言語プレーンでは、U+D800 から U+DFFF までのコード ポイント ブロックは永続的に予約されており、Unicode 文字にはマップされません。 UTF-16 は、0xD800-0xDFFF セクションの予約されたコード ビットを使用して、補助プレーン文字のコード ビットをエンコードします。

最も一般的に使用される文字は BMP に含まれており、2 バイトで表されます。補助プレーンのコード ビットは、サロゲート ペアと呼ばれる、UTF-16 の 16 ビット長のコード要素のペアとしてエンコードされます。具体的な方法は次のとおりです。

  • コード ビットを 0x10000 で減算します。取得される値は 0 から 0xFFFFF までの 20 ビット長です。

  • 上位 10 ビット値 (値の範囲は 0~0x3FF) は、最初のコード要素を取得するために 0xD800 に追加されるか、上位サロゲートと呼ばれます。値の範囲は 0xD800~0xDBFF であるためです。下位サロゲートよりも小さいため、混乱を避けるために、Unicode 標準では上位サロゲートをリード サロゲートと呼ぶようになりました。

  • 下位 10 ビット値 (値の範囲も 0~0x3FF) は、2 番目のシンボルを取得するために 0xDC00 に追加されるか、下位サロゲートと呼ばれます。上位サロゲートよりも大きいため、混乱を避けるために、Unicode 標準では下位サロゲートをトレイル サロゲートと呼ぶようになりました。

たとえば、U+10437 エンコード:

  • 0×10437 から 0×10000 を引くと、結果は 0×00437 となり、バイナリでは 0000 0000 0100 0011 0111 となります。

  • 上位 10 ビット値と下位 10 ビット値を分割します (バイナリを使用): 0000000001 と 0000110111。

  • 上位値に 0xD800 を追加して上位ビットを形成します: 0xD800 + 0x0001 = 0xD801。

  • 下位の値に 0xDC00 を加算して、下位ビットを形成します: 0xDC00 + 0x0037 = 0xDC37。

BMP 内の先頭サロゲート、末尾サロゲート、および有効な文字のコード ポイントは互いに重なり合わないため、ある文字エンコーディングの一部が別の文字の異なる部分と重なることは不可能です検索時のエンコーディング。したがって、1 つのコード要素 (コード ポイントを構成する基本単位、2 バイト) だけを調べるだけで、特定の文字の次の文字の開始コード要素を判断できます。

Java に関連するコードポイント

文字列オブジェクトの場合、その内容は char 配列を通じて保存されます。 char 型は 2 バイトに格納され、この 2 バイトには実際に UTF-16 エンコーディングでコード要素が格納されます。 charAt メソッドと length メソッドを使用した場合、実際に返されるのはコード要素とコード要素数です。通常は問題ありませんが、文字が補助平面文字の場合、上記 2 つのメソッドは正しい結果を取得できません。 。正しい処理方法は次のとおりです。

int character = aString.codePointAt(i);
int length = aString.codePointCount(0, aString.length());

codePointAt の戻り値は char ではなく int であることに注意してください。この値は Unicode コードです。

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。