Является ли UTF8 инъективным отображением? - PullRequest
6 голосов
/ 14 ноября 2011

Мы пишем C++ приложение и должны знать это:

Является ли текст UTF8 кодирующим инъективным отображением байтов в символы, что означает, что каждый отдельный символ (буква ...) кодируется только одним способом? Так, например, буква «Ž» не может быть закодирована, скажем, как 3231 и 32119.

Ответы [ 5 ]

13 голосов
/ 14 ноября 2011

Это очень сильно зависит от того, что вы считаете «буквой».

UTF8 - это, по сути, крошечный кусочек Unicode.

В основном есть как минимум три уровня: байты, кодточки и графемы кластеров.Кодовая точка может быть закодирована одним или несколькими байтами в соответствии с определенной кодировкой, такой как UTF8, UTF16 или UTF32.Эта кодировка уникальна (потому что все альтернативные способы объявлены недействительными).Однако кодовая точка не всегда является глифом, потому что есть так называемые комбинирующие символы.Такие комбинирующие символы следуют за базовым символом и, как следует из их названия, объединяются с базовым символом.Например, есть комбинирующий символ U + 0308 ДИАРЕЗ КОМБИНИРОВАНИЯ, который ставит диарез (¨) над предыдущей буквой.Таким образом, если он следует, например, a (U + 0061 LATIN SMALL LETTER A), результатом является ä.Однако есть также одна кодовая точка для буквы ä (U + 00E4 МАЛЕНЬКОЕ ПИСЬМО А С ДИАРЕЗОМ), поэтому это означает, что кодовые последовательности U + 0061 U + 0308 и U + 00E4 описывают одну и ту же букву.

Таким образом, каждая кодовая точка имеет единственную действительную кодировку UTF 8 (например, U + 0061 это «\ 141», U + 0308 это «\ 314 \ 210» и U + 00e4 это «\ 303 \ 244», но буква äкодируется как последовательностью кодовых точек U + 0061 U + 0308, то есть в UTF8, байтовой последовательностью "\ 141 \ 314 \ 210", так и одиночной кодовой точкой U + 00E4, то есть байтовой последовательностью "\ 303 \ 244".

Что еще хуже, поскольку производители Unicode решили, что объединяющие буквы следуют за базовой буквой, а не перед ней, вы не сможете узнать, завершен ли ваш глиф, пока не увидите следующая кодовая точка (если это не комбинация кодовой точки, ваше письмо закончено).

6 голосов
/ 14 ноября 2011

Действительный UTF-8 действительно кодирует каждый символ уникально. Однако существуют так называемые сверхдлинные последовательности, которые соответствуют общей схеме кодирования, но являются недопустимыми по определению, поскольку для кодирования символа может использоваться только самая короткая последовательность.

Например, есть производная от UTF-8, называемая модифицированная UTF-8, которая кодирует NUL в виде слишком длинной последовательности 0xC0 0x80 вместо 0x00, чтобы получить кодировку, совместимую со строками с нулевым символом в конце.

Если вы спрашиваете о графемных кластерах (то есть воспринимаемых пользователем символах) вместо символов, то даже допустимый UTF-8 является неоднозначным. Однако Unicode определяет несколько различных форм нормализации , и если вы ограничиваете себя нормализованными строками, то UTF-8 действительно является инъективным.

Несколько не по теме: Вот несколько примеров ASCII-искусства, которые я придумал, чтобы помочь визуализировать различные концепции символа . Вертикально разделены уровень человек , абстрактный и машина . Не стесняйтесь придумывать лучшие имена ...

                         [user-perceived characters]<-+
                                      ^               |
                                      |               |
                                      v               |
            [characters] <-> [grapheme clusters]      |
                 ^                    ^               |
                 |                    |               |
                 v                    v               |
[bytes] <-> [codepoints]           [glyphs]<----------+

Чтобы вернуться к теме: Этот график также показывает, где могут возникать возможные проблемы при использовании байтов для сравнения абстрактных строк. В частности (при условии UTF-8) программист должен убедиться, что

  • последовательность байтов действительна, т.е. не содержит слишком длинных последовательностей и не кодирует не символьные кодовые точки
  • последовательность символов нормализована, поэтому эквивалентные кластеры графем имеют уникальное представление
3 голосов
/ 14 ноября 2011

Для начала нужна терминология:

  • Буква : (абстрактная концепция, не в Unicode) какая-либо буква или символ, который вы хотите представить.
  • Кодовая точка : число, связанное с символом Unicode.
  • Кластер графем : последовательность кодов Unicode, которые соответствуют одной букве, например: a + ́ для буквы á.
  • Glyph : (концепция на уровне шрифтов, не в Unicode): графическое представление буквы.

Каждая кодовая точка (например, U + 1F4A9) получает уникальное представление в виде байтов в UTF-8 (например, 0xF0 0x9F 0x92 0xA9).

Некоторые буквы могут быть представлены несколькими различными способами в виде кодовых точек (то есть как различные кластеры графем). Например: á может быть представлено как одна кодовая точка á (LATIN SMALL LETTER A WITH ACUTE), или это может быть представлено как кодовая точка для a (LATIN SMALL LETTER A) + кодовая точка для ́ ( КОМБИНИРОВАНИЕ ОСТРОГО АКЦЕНТА). Юникод имеет несколько канонических форм нормализации, чтобы справиться с этим (например: NFC или каноническая форма нормализации C - это свободно форма нормализации с меньшим количеством кодовых точек, в то время как NFD полностью разложена).

И затем, есть также лигатуры (например: ) и некоторые другие связанные с представлением варианты буквы (например: верхние индексы, пробелы без перерывов, буквы различной формы в разных местах слова, .. .). Некоторые из них в Unicode, чтобы разрешить преобразование туда и обратно без потерь из старых в наборы символов. Unicode имеет формы нормализации совместимости (NFKC и NFKD), чтобы справиться с этим.

2 голосов
/ 14 ноября 2011

Да. UTF-8 - это просто стандартный способ кодирования символов Unicode. Это было сделано для того, чтобы был только один способ кодировать каждый из символов Unicode.

Немного не по теме: может быть полезно знать, что некоторые символы очень похожи по внешнему виду (на людей), но они все же различаются - например, в кириллице есть знак, который выглядит очень похоже на '/' ,

0 голосов
/ 14 ноября 2011

Да, вроде.При правильном использовании каждая кодовая точка Unicode должна кодироваться только одним способом в UTF-8, но это отчасти из-за требования, что только самая короткая применимая последовательность байтов UTF-8 должна использоваться для любого символа.

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

Например, 'Z' может быть закодирован как 0x5a или {0xa1, 0x9a} (среди прочих), хотя единственный 0x5a считается правильным, потому что это самая короткая последовательность.

...