Кодировка Unicode Java

Java char 2 байта (максимальный размер 65 536), но есть 95 221 символов Юникода. Означает ли это, что вы не можете обрабатывать определенные символы Unicode в приложении Java?

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

6 ответов

Решение

Вы можете справиться со всеми, если вы достаточно осторожны.

в Java char является кодовым блоком UTF-16. Для символов с кодовой точкой> 0xFFFF он будет закодирован с 2 charс (суррогатная пара).

См. http://www.oracle.com/us/technologies/java/supplementary-142654.html чтобы узнать, как обрабатывать эти символы в Java.

(Кстати, в Unicode 5.2 есть 107 154 назначенных символа из 1114 112 слотов.)

Java использует UTF-16. Единственная Java char может представлять только символы из основной многоязычной плоскости. Другие символы должны быть представлены суррогатной парой из двух char s. Это отражено методами API, такими как String.codePointAt(),

И да, это означает, что большая часть кода Java будет так или иначе ломаться при использовании с символами вне базовой многоязычной плоскости.

Чтобы добавить к другим ответам, некоторые моменты, которые нужно помнить:

  • Java char занимает всегда 16 бит.

  • Символ Unicode, когда кодируется как UTF-16, занимает "почти всегда" (не всегда) 16 битов, потому что существует более 64K символов Unicode. Следовательно, символ Java НЕ является символом Юникода (хотя "почти всегда").

  • Выше "почти всегда" означает 64K первых кодовых точек Unicode в диапазоне от 0x0000 до 0xFFFF ( BMP), которые занимают 16 бит в кодировке UTF-16.

  • Не-BMP ("редкий") символ Unicode представлен двумя символами Java (суррогатное представление). Это относится также к буквальному представлению в виде строки: например, символ U+20000 записывается как "\ uD840 \ uDC00".

  • Corolary: string.length() возвращает количество символов java, а не символов Unicode. Строка, содержащая только один "редкий" символ Юникода (например, U+20000), вернет length() = 2, То же самое относится и к любому методу, который работает с последовательностями символов.

  • Java обладает небольшим интеллектом для работы с не-BMP символами юникода в целом. Есть несколько служебных методов, которые обрабатывают символы как кодовые точки, представленные как целые, например: Character.isLetter(int ch), Это настоящие полностью Unicode методы.

Вы сказали:

Символ Java составляет 2 байта (максимальный размер 65536), но содержит 95221 символ Unicode.

Юникод растет

На самом деле перечень символов, определенных в Unicode, резко вырос. Unicode продолжает расти - и не только из-за смайликов.

  • 143859 символов в Unicode 13 (Java 15, примечания к выпуску)
  • 137994 символа в Unicode 12.1 (Java 13 и 14)
  • 136755 символов в Unicode 10 (Java 11 и 12)
  • 120737 символов в Unicode 8 (Java 9)
  • 110182 символа в Unicode 6.2 (Java 8)
  • 109449 символов в Unicode 6.0 (Java 7)
  • 96447 символов в Unicode 4.0 (Java 5 и 6)
  • 49259 символов в Unicode 3.0 (Java 1.4)
  • 38952 символа в Unicode 2.1 (Java 1.1.7)
  • 38950 символов в Unicode 2.0 (Java 1.1)
  • 34233 символа в Unicode 1.1.5 (Java 1.0)

char это наследие

В charТип давно устарел, теперь унаследованный.

Используйте номера кодовой точки

Вместо этого вы должны работать с номерами кодовых точек.


Ты спрашивал:

Означает ли это, что вы не можете обрабатывать определенные символы Unicode в приложении Java?

В char type может адресовать менее половины сегодняшних символов Unicode.

Для представления любого символа Юникода используйте числа кодовой точки. Никогда не использоватьchar.

Каждому символу в Юникоде присваивается номер кодовой точки. Их диапазон превышает миллион, от 0 до 1,114,112. Выполнение математических расчетов при сравнении с числами, перечисленными выше, означает, что большинство чисел в этом диапазоне еще не присвоены символу. Некоторые из этих номеров зарезервированы как зоны частного использования и никогда не будут назначены.

В String класс получил методы для работы с номерами кодовых точек, как и Character класс.

Получите номер кодовой точки для любого символа в строке по номеру индекса, отсчитываемому от нуля. Вот получаем97 для письма a.

int codePoint = "Cat".codePointAt( 1 ) ; // 97 = 'a', hex U+0061, LATIN SMALL LETTER A.

Для более общего CharSequence скорее, чем String, используйте Character.codePointAt.

Мы можем получить имя Unicode для номера кодовой точки.

String name = Character.getName( 97 ) ; // letter `a`

ЛАТИНСКАЯ СТРОЧНАЯ БУКВА A

Мы можем получить поток номеров кодовых точек всех символов в строке.

IntStream codePointsStream = "Cat".codePoints() ;

Мы можем превратить это в List из Integerобъекты. См. Как преобразовать Java 8 IntStream в список?.

List< Integer > codePointsList = codePointsStream.boxed().collect( Collectors.toList() ) ;

Любой номер кодовой точки можно заменить на String одного персонажа, позвонив Character.toString.

String s = Character.toString( 97 ) ; // 97 is `a`, LATIN SMALL LETTER A. 

а

Мы можем произвести String объект из IntStream номеров кодовых точек. См. Раздел Создание строки из IntStream номеров кодовых точек?.

IntStream intStream = IntStream.of( 67 , 97 , 116 , 32 , 128_008 ); // 32 = SPACE, 128,008 = CAT (emoji).

String output =
        intStream
                .collect(                                     // Collect the results of processing each code point.
                        StringBuilder :: new ,                // Supplier<R> supplier
                        StringBuilder :: appendCodePoint ,    // ObjIntConsumer<R> accumulator
                        StringBuilder :: append               // BiConsumer<R,​R> combiner
                )                                             // Returns a `CharSequence` object.
                .toString();                                  // If you would rather have a `String` than `CharSequence`, call `toString`. 

Кошка


Ты спрашивал:

Сводится ли это к тому, какую кодировку символов вы используете?

Внутри Stringв Java всегда используется UTF-16.

Вы используете только другую кодировку символов при импорте или экспорте текста в строки Java или из них.

Итак, отвечая на ваш вопрос, нет, кодировка символов здесь напрямую не связана. Как только вы поместите свой текст в JavaString, он находится в кодировке UTF-16 и поэтому может содержать любой символ Unicode. Конечно, чтобы увидеть этот символ, вы должны использовать шрифт с глифом, определенным для этого конкретного символа.

Если при экспорте текста из строк Java вы укажете устаревшую кодировку символов, которая не может представлять некоторые символы Unicode, используемые в вашем тексте, у вас возникнет проблема. Поэтому используйте современную кодировку символов, которая в настоящее время означает UTF-8, поскольку UTF-16 теперь считается вредным.

Взгляните на поддержку Unicode 4.0 в статье J2SE 1.5, чтобы узнать больше об уловках, изобретенных Sun для обеспечения поддержки всех кодовых точек Unicode 4.0.

В итоге вы найдете следующие изменения для Unicode 4.0 в Java 1.5:

  • char кодовая единица UTF-16, а не кодовая точка
  • новые API низкого уровня используют int представлять кодовую точку Unicode
  • API высокого уровня были обновлены для понимания суррогатных пар
  • предпочтение API последовательностей символов вместо методов на основе символов

Поскольку в Java нет 32-битных символов, я позволю вам судить, можем ли мы назвать эту хорошую поддержку Юникода.

Вот документация Oracle по символьным представлениям Unicode. Или, если вы предпочитаете, более подробную документацию здесь.

Тип данных char (и, следовательно, значение, которое инкапсулирует объект Character) основан на исходной спецификации Unicode, в которой символы определены как 16-битные объекты фиксированной ширины. С тех пор стандарт Unicode был изменен, чтобы разрешить символы, представление которых требует более 16 бит. Диапазон допустимых кодовых точек теперь составляет от U+0000 до U+10FFFF, известный как скалярное значение Unicode. (См. Определение обозначения U+n в стандарте Unicode.)

Набор символов от U+0000 до U+FFFF иногда называют базовой многоязычной плоскостью (BMP). Символы, кодовые точки которых больше, чем U+FFFF, называются дополнительными символами. Платформа Java 2 использует представление UTF-16 в массивах символов и в классах String и StringBuffer. В этом представлении дополнительные символы представляются в виде пары значений символов, первое из диапазона верхних суррогатов (\uD800-\uDBFF), второе из диапазона нижних суррогатов (\uDC00-\uDFFF).

Следовательно, значение символа представляет кодовые точки базовой многоязычной плоскости (BMP), включая суррогатные кодовые точки или кодовые единицы кодирования UTF-16. Значение int представляет все кодовые точки Unicode, включая дополнительные кодовые точки. Младшие (младшие значащие) 21 бит типа int используются для представления кодовых точек Unicode, а верхние (младшие значащие) 11 битов должны быть равны нулю. Если не указано иное, поведение в отношении дополнительных символов и значений суррогатных символов является следующим:

  • Методы, которые принимают только значение символа, не могут поддерживать дополнительные символы. Они обрабатывают значения символов из суррогатных диапазонов как неопределенные символы. Например, Character.isLetter('\uD840') возвращает значение false, даже если это конкретное значение, если за ним следует любое низкосуррогатное значение в строке, будет представлять букву.
  • Методы, принимающие значение типа int, поддерживают все символы Unicode, включая дополнительные символы. Например, Character.isLetter(0x2F81A) возвращает true, поскольку значение кодовой точки представляет букву (идеограф CJK).

Из документации OpenJDK7 для String:

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

Другие вопросы по тегам