Java charAt используется с символами, которые имеют два блока кода

Из Core Java, vol. 1, 9-е изд., С. 69:

Для символа ℤ требуется кодирование в кодировке UTF-16. Вызов

String sentence = "ℤ is the set of integers"; // for clarity; not in book
char ch = sentence.charAt(1)

не возвращает пробел, а второй блок кода ℤ.

Но кажется, что sentence.charAt(1) возвращает пробел. Например, оператор if в следующем коде оценивается как true.

String sentence = "ℤ is the set of integers";
if (sentence.charAt(1) == ' ')
    System.out.println("sentence.charAt(1) returns a space");

Почему?

Я использую JDK SE 1.7.0_09 на Ubuntu 12.10, если это имеет значение.

Ответы

Ответ 1

Похоже, что в книге говорится, что "ℤ" не является символом UTF-16 в базовом многоязычном плане , но на самом деле это так.

Java использует UTF-16 с суррогатными парами для символов, которые не находятся в базовой многоязычной плоскости. Поскольку "ℤ" (0x2124) находится в базовой многоязычной плоскости, он представлен единым блоком кода. В вашем примере sentence.charAt(0) вернет 'ℤ', а sentence.charAt(1) вернет '.

Символ, представленный суррогатными парами, имеет два элемента кода, составляющих символ. sentence.charAt(0) вернет первый блок кода, а sentence.charAt(1) вернет второй блок кода.

См. http://docs.oracle.com/javase/6/docs/api/java/lang/String.html:

A String представляет строку в формате UTF-16, в которой дополнительные символы представлены суррогатными парами (см. разделение символов Unicode в классе символов для больше информации). Значения индекса относятся к единицам кода char, поэтому дополнительный символ использует две позиции в строке.

Ответ 2

В соответствии с документацией String представляется внутри как utf-16, поэтому charAt() предоставляет вам две кодовые точки. Если вы заинтересованы в просмотре отдельных кодовых пунктов, вы можете использовать этот код (из этого answer):

final int length = sentence.length();
for (int offset = 0; offset < length; ) {
   final int codepoint = sentence.codePointAt(offset);

   // do something with the codepoint

   offset += Character.charCount(codepoint);
}

Ответ 3

Javadocs Объясните это:

A String представляет строку в формате UTF-16, в которой дополнительные символы представлены суррогатными парами (см. разделение символов Unicode в классе символов для больше информации). Значения индекса относятся к единицам кода char, поэтому дополнительный символ использует две позиции в строке.

Короче говоря, книга неверна.Забастовкa >

Изменить, чтобы добавить из комментариев ниже: Что-то, о чем я не думал прошлой ночью, было то, что персонаж, который вы использовали в своем вопросе, на самом деле не тот, о котором они говорят, и что они действительно получают, когда у вас есть символ, которому требуется четыре байта, а не два. Приведенный выше параграф в Javadoc ссылается на другой javadoc; Unicode Character Representations, в котором говорится о последствиях этого.

Ответ 4

Хорстманн говорил о "Z", которому нужны два кодовых блока UTF-16. Посмотрите на этот код:

public class Main {
    public static void main(String[] args)
    {
        String a = "\uD83D\uDE02 is String";
        System.out.println("Length: " + a.length());
        System.out.println(a.charAt(0));
        System.out.println(a.charAt(1));
        System.out.println(a.charAt(2));
        System.out.println(a.charAt(3));
    }
}

в IntelliJ Idea я даже не могу вставить 4-байтовый символ как один символ, потому что при вставке этого смайлика: 😂 IDE автоматически преобразует его в: "\ uD83D\uDE02". Обратите внимание, что этот эмодзи считается за 2 символа.

Взгляните на: Какие наиболее распространенные не-BMP символы Unicode в действительности используются?