Что такое "суррогатная пара" в Java?

Я читал документацию для StringBufferв частности метод reverse(). Эта документация упоминает что-то о суррогатных парах. Что такое суррогатная пара в этом контексте? А что такое низкие и высокие суррогаты?

5 ответов

Решение

Термин "суррогатная пара" относится к средству кодирования символов Unicode с высокими кодовыми точками в схеме кодирования UTF-16.

В кодировке Unicode символы отображаются в значения от 0x0 до 0x10FFFF.

Внутренне Java использует схему кодирования UTF-16 для хранения строк текста Unicode. В UTF-16 используются 16-битные (двухбайтовые) кодовые единицы. Поскольку 16 бит могут содержать только диапазон символов от 0x0 до 0xFFFF, некоторая дополнительная сложность используется для хранения значений выше этого диапазона (от 0x10000 до 0x10FFFF). Это делается с помощью пар кодовых единиц, известных как суррогаты.

Единицы суррогатного кода находятся в двух диапазонах, известных как "высокие суррогаты" и "низкие суррогаты", в зависимости от того, разрешены ли они в начале или в конце последовательности из двух единиц кода.

Ранние версии Java представляли символы Unicode, используя 16-битный тип данных char. Этот дизайн имел смысл в то время, потому что все символы Unicode имели значения меньше 65 535 (0xFFFF) и могли быть представлены в 16 битах. Позже, однако, Unicode увеличил максимальное значение до 1,114,111 (0x10FFFF). Поскольку 16-разрядные значения были слишком малы для представления всех символов Unicode в версии 3.1 Unicode, 32-разрядные значения, называемые кодовыми точками, были приняты для схемы кодирования UTF-32. Но 16-битные значения предпочтительнее 32-битных для эффективного использования памяти, поэтому Unicode представил новый дизайн, позволяющий продолжить использование 16-битных значений. Эта конструкция, принятая в схеме кодирования UTF-16, присваивает 1024 значения 16-разрядным старшим суррогатам (в диапазоне U+D800 - U+DBFF) и еще 1024 значения 16-разрядным старшим суррогатам (в диапазоне U+DC00) до U+DFFF). Он использует высокий суррогат, за которым следует низкий суррогат - суррогатная пара - для представления (произведение 1024 и 1024)1,048,576 (0x100000) значений между 65,536 (0x10000) и 1,114,111 (0x10FFFF).

Добавление дополнительной информации к ответам из этого поста

Протестировано в Java-12, должно работать во всех версиях Java выше 5.

Как упомянуть здесь: /questions/41742674/chto-takoe-surrogatnaya-para-v-java/41742697#41742697,
какой бы символ (чей Unicode был выше U+FFFF) представлен как суррогатная пара, которую Java хранит в виде пары значений char, т.е. один символ Unicode представляется как два смежных символа java.
Как мы видим в следующем примере.
1. Длина:

"".length()  //2, Expectations was it should return 1

"".codePointCount(0,"".length())  //1, To get number of Unicode character in Java String  

2. Равенство:
Представлять "" в строку, используя Unicode \ud83c\udf09 как ниже и проверьте равенство.

"".equals("\ud83c\udf09") // true

Java не поддерживает UTF-32

"".equals("\u1F309") // false  

3. Вы можете преобразовать символ Unicode в строку Java

"".equals(new String(Character.toChars(0x0001F309))) //true

4. String.substring() не учитывает дополнительные символы

"".substring(0,1) //"?"
"".substring(0,2) //""
"".substring(0,4) //""

Чтобы решить это, мы можем использовать String.offsetByCodePoints(int index, int codePointOffset)

"".substring(0,"".offsetByCodePoints(0,1) // ""
"".substring(2,"".offsetByCodePoints(1,2)) // ""

5. Итерация строки Юникода с BreakIterator
6. Сортировка строк с помощью Юникода java.text.Collator
7. Символы в верхний регистр, нижний регистр не должны использоваться, вместо этого используйте строчные буквы верхнего и нижнего регистра определенной локали
8. Character.isLetter(char ch) не поддерживает, лучше использовать Character.isLetter(int codePoint), для каждого methodName(char ch) Метод в классе Character будет иметь тип methodName(int codePoint) который может обрабатывать дополнительные символы.
9. Укажите кодировку в String.getBytes(), конвертируя из байтов в строку, InputStreamReader, OutputStreamWriter

Ref:
https://coolsymbol.com/emojis/emoji-for-copy-and-paste.html
https://www.online-toolz.com/tools/text-unicode-entities-convertor.php
https://www.ibm.com/developerworks/library/j-unicode/index.html
https://www.oracle.com/technetwork/articles/javaee/supplementary-142654.html

Больше информации на примере image1 image2

В документации говорится, что недопустимые строки UTF-16 могут стать действительными после вызова reverse метод, поскольку они могут быть реверсами допустимых строк. Суррогатная пара (обсуждаемая здесь) - это пара 16-битных значений в UTF-16, которые кодируют одну кодовую точку Unicode; низкие и высокие суррогаты - это две половины этого кодирования.

Небольшое предисловие

  • Юникод представляет собой кодовые точки. Каждая кодовая точка может быть закодирована в 8-, 16- или 32-битные блоки в соответствии со стандартом Unicode.
  • До версии 3.1 в основном использовалось 8-битное кодирование, известное как UTF-8, и 16-битное кодирование, известное как UCS-2 или "Универсальный набор символов, закодированный в 2 октета". UTF-8 кодирует точки Unicode как последовательность 1-байтовых блоков, тогда как UCS-2 всегда занимает 2 байта:

    A = 41 - один блок из 8 бит с UTF-8
    A = 0041 - один блок из 16 бит с UCS-2
    Ω = CE A9 - два блока по 8 бит с UTF-8
    Ω = 03A9 - один блок из 16 бит с UCS-2

Проблема

Консорциум считал, что 16 бит будет достаточно для охвата любого удобочитаемого языка, что дает 2^16 = 65536 возможных кодовых значений. Это было верно для плоскости 0, также известной как BPM или базовая многоязычная плоскость, которая включает сегодня 55445 из 65536 кодовых точек. BPM охватывает почти все человеческие языки в мире, включая китайско-японско-корейские символы (CJK).

Прошло время, и были добавлены новые наборы азиатских символов, только на китайские символы ушло более 70 000 очков. Теперь в стандарте есть даже очки Emoji. Добавлены новые 16 "дополнительных" самолетов. Помещения UCS-2 было недостаточно, чтобы укрыть что-то большее, чем Самолет-0.

Решение Unicode

  1. Ограничьте Unicode до 17 плоскостей × 65536 символов на плоскость = 1 114 112 максимальных баллов.
  2. Присутствует UTF-32, ранее известный как UCS-4, для хранения 32 бита для каждой кодовой точки и покрытия всех плоскостей.
  3. Продолжайте использовать UTF-8 в качестве динамического кодирования, ограничьте UTF-8 максимум 4 байтами для каждой кодовой точки, то есть от 1 до 4 байтов на точку.
  4. Устарела UCS-2
  5. Создайте UTF-16 на основе UCS-2. Сделайте UTF-16 динамическим, чтобы он занимал 2 или 4 байта на точку. Назначьте 1024 точки U+D800–U+DBFF, называемые высокими суррогатами, для UTF-16; назначьте 1024 символа U+DC00–U+DFFF, называемые младшими суррогатами, для UTF-16.

    С этими изменениями, BPM покрывается 1 блоком из 16 бит в UTF-16, в то время как все "Дополнительные символы" покрываются суррогатными парами, представляющими 2 блока по 16 бит каждый, всего 1024x1024 = 1 048 576 точек.

    Высокий суррогат предшествует низкому суррогату. Любое отклонение от этого правила считается плохой кодировкой. Например, суррогат без пары неверен, низкий суррогат, стоящий перед высоким суррогатом, неверен.

    , 'MUSICAL SYMBOL G CLEF', кодируется в UTF-16 как пара суррогатов 0xD834 0xDD1E (2 на 2 байта),
    в UTF-8 как 0xF0 0x9D 0x84 0x9E (4 на 1 байт),
    в UTF-32 как 0x0001D11E (1 на 4 байта).

Текущая ситуация

  • Хотя в соответствии со стандартом суррогаты специально назначаются только UTF-16, исторически некоторые приложения Windows и Java использовали точки UTF-8 и UCS-2, зарезервированные теперь для суррогатного диапазона.
    Для поддержки устаревших приложений с неправильными кодировками UTF-8/UTF-16 был создан новый стандарт WTF-8, Wobbly Transformation Format. Он поддерживает произвольные суррогатные точки, такие как непарный суррогат или неправильная последовательность. Сегодня некоторые продукты не соответствуют стандарту и рассматривают UTF-8 как WTF-8.
  • Суррогатное решение открыло множество проблем безопасности при преобразовании между разными кодировками, большинство из них были обработаны хорошо.

Многие исторические детали были скрыты, чтобы соответствовать теме ⚖.
Последнюю версию стандарта Unicode можно найти по адресу http://www.unicode.org/versions/latest.

Суррогатные пары относятся к способу кодирования определенных символов в UTF-16, см. http://en.wikipedia.org/wiki/UTF-16/UCS-2.

Суррогатная пара - это две "кодовые единицы" в UTF-16, которые составляют одну "кодовую точку". В документации Java говорится, что эти "кодовые точки" все еще будут действительны, а их "кодовые единицы" будут упорядочены правильно после обратного. Далее говорится, что две непарные единицы суррогатного кода могут быть обращены и образовать действительную суррогатную пару. Это означает, что если есть непарные кодовые блоки, то есть вероятность, что обратная сторона реверса может не совпадать!

Однако обратите внимание, что в документации ничего не говорится о графемах - которые объединяют несколько кодовых точек. Это означает, что e и акцент, который идет вместе с ним, могут все еще быть переключены, таким образом, помещая акцент перед e. Это означает, что если перед е есть еще один гласный, он может получить ударение, которое было на е.

Хлоп!

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