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 в действительности используются?