Чи є UTF-16 фіксованої або змінної ширини? Чому UTF-8 не має проблеми з порядком байт?


16
  1. Чи є UTF-16 фіксованої або змінної ширини? Я отримав різні результати з різних джерел:

    Від http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF :

    UTF-16 зберігає символи Unicode в шістнадцять бітових фрагментах.

    З http://en.wikipedia.org/wiki/UTF-16/UCS-2 :

    UTF-16 (16-розрядний формат перетворення Unicode) - це кодування символів для Unicode, здатне кодувати 1,112,064 [1] числа (звані кодові точки) у просторі коду Unicode від 0 до 0x10FFFF. Це створює результат змінної довжини одного або двох 16-бітних одиниць коду на кодову точку.

  2. З першого джерела

    Перевага UTF-8 також має перевагу в тому, що одиницею кодування є байт, тому проблем впорядкування байтів немає.

    Чому UTF-8 не має проблеми з порядком байт? Він змінної ширини, і один символ може містити більше одного байту, тому я думаю, що порядок байтів все ще може бути проблемою?

Дякую та з повагою!


Відповіді:


13

(1) Що означає послідовність байтів, довільна таблиця знаків у С? Чи є UTF-16 послідовністю байтів, або що це тоді? (2) Чому послідовність байтів не має нічого спільного зі змінною довжиною?

Ви, здається, нерозумієте, що таке ендіанські проблеми Ось короткий підсумок.

32-бітове ціле число займає 4 байти. Тепер ми знаємо логічне впорядкування цих байтів. Якщо у вас є 32-бітове ціле число, ви можете отримати високий байт цього коду за допомогою наступного коду:

uint32_t value = 0x8100FF32;
uint8_t highByte = (uint8_t)((value >> 24) & 0xFF); //Now contains 0x81

Це все добре і добре. Звідки починається проблема - це те, як різні апаратури зберігають і витягують цілі числа з пам'яті.

У порядку Big Endian 4-байтний об'єм пам'яті, який ви читаєте як 32-бітове ціле число, буде прочитаний, причому перший байт буде високим байтом:

[0][1][2][3]

У порядку Little Endian 4-байтний об'єм пам'яті, який ви читаєте як 32-бітове ціле число, буде прочитаний, причому перший байт буде низьким байтом:

[3][2][1][0]

Якщо у вас є вказівник на вказівник на 32-бітове значення, ви можете зробити це:

uint32_t value = 0x8100FF32;
uint32_t *pValue = &value;
uint8_t *pHighByte = (uint8_t*)pValue;
uint8_t highByte = pHighByte[0]; //Now contains... ?

Згідно з C / C ++, результат цього не визначений. Це може бути 0x81. Або це може бути 0x32. Технічно він може повернути що завгодно, але для реальних систем він поверне ту чи іншу.

Якщо у вас є вказівник на адресу пам'яті, ви можете прочитати цю адресу як 32-бітове значення, 16-бітове значення або 8-бітове значення. На великій ендіанській машині вказівник вказує на високий байт; на маленькій ендіанській машині вказівник вказує на низький байт.

Зауважте, що це все стосується читання та запису до / з пам'яті. Це не має нічого спільного з внутрішнім кодом C / C ++. Перша версія коду, та, яку C / C ++ не оголошує невизначеною, завжди працюватиме, щоб отримати високий байт.

Проблема полягає в тому, коли ви починаєте читати байтові потоки. Такі як з файлу.

16-бітні значення мають ті ж проблеми, що і 32-бітні; у них просто 2 байти замість 4. Отже, файл може містити 16-бітні значення, що зберігаються у великому ендіані чи малому ендіані.

UTF-16 визначається як послідовність 16-бітних значень . Ефективно, це uint16_t[]. Кожна одиниця коду - це 16-бітове значення. Тому, щоб правильно завантажити UTF-16, ви повинні знати, що таке цінність даних.

UTF-8 визначається як послідовність 8-бітних значень . Це a uint8_t[]. Кожна одиниця коду має розмір 8 біт: один байт.

Тепер і UTF-16, і UTF-8 дозволяють множині кодових одиниць (16-бітне або 8-бітове значення) об'єднатись разом, щоб утворити кодову точку Unicode ("символ", але це не правильний термін; це спрощення) ). Порядок цих кодових блоків , які утворюють елемент коду диктується UTF-16 і UTF-8 кодуванні.

Обробляючи UTF-16, ви читаєте 16-бітове значення, виконуючи будь-яке ендіанське перетворення. Потім ви виявляєте, чи це сурогатна пара; якщо це так, то ви читаєте ще 16-бітове значення, комбінуєте два, і з цього ви отримуєте значення кодової точки Unicode.

Під час обробки UTF-8 ви читаєте 8-бітове значення. Неможливо перетворити ендіан, оскільки існує лише один байт. Якщо перший байт позначає багатобайтову послідовність, то ви читаєте деяку кількість байтів, як це продиктовано багатобайтовою послідовністю. Кожен окремий байт є байтом і тому не має конверсії ендіан. Порядок цих байтів в послідовності, так само , як порядок сурогатних пар в UTF-16, визначається UTF-8.

Отже, з UTF-8 не може бути ніяких проблем.


10

Відповідь Джеремі Банка є правильною, наскільки це йдеться, але він не звертався до упорядкування байтів.

Коли ви використовуєте UTF-16, більшість гліфів зберігаються за допомогою двобайтового слова - але коли це слово зберігається у файлі диска, який порядок ви використовуєте для зберігання складових байтів?

Як приклад, CJK (китайський) гліф для слова "вода" має кодування UTF-16 у шістнадцятковій кількості 6C34. Коли ви пишете, що як два байти на диск, ви пишете це як "big-endian" (два байти - 6C 34)? Або ви пишете це як "малопомітний" (два байти - 34 6С)?

З UTF-16 обидва впорядкування є законними, і ви зазвичай вказуєте, який саме файл має, зробивши перше слово у файлі позначка порядку в байтах (BOM), що для кодування великого ендіану - це FE FF, а для маленького-endian кодування FF FE.

UTF-32 має ту саму проблему та те саме рішення.

У UTF-8 немає цієї проблеми, оскільки вона має змінну довжину, і ви ефективно записуєте послідовність байтів гліфа так, ніби вона є малопомітною. Наприклад, літера "P" завжди кодується за допомогою одного байта - 80 -, а символ заміни завжди кодується за допомогою двох байтів FF FD у цьому порядку.

Деякі програми ставлять трибайтовий індикатор (EF BB BF) на початок файлу UTF-8, що допомагає відрізнити UTF-8 від подібних кодувань, таких як ASCII, але це не дуже часто, крім випадків у MS Windows.


Спасибі! (1) літера "P" - це лише один байт у UTF-8. Чому в його код додається символ заміни? (2) У UTF-8 є інші символи, які мають більше одного байту в UTF-8. Чому порядок байт між байтами для кожного такого символу не є проблемою?
StackExchange для всіх

@Tim: (1) Ви не додаєте символ заміни до коду для P. Якщо ви бачите 80 FF FD, це два символи - символ P та символ заміни.
Боб Мерфі

(2) Ви завжди записуєте та читаєте два байти для "символу заміни" як FF FD у такому порядку. Проблема впорядкування байтів виникне лише в тому випадку, якщо ви також можете написати "символ заміни" як FD FF - але ви не можете; ця послідовність з двох байтів була б чимось іншим, ніж "символ заміни".
Боб Мерфі

1
@Tim: Можливо, ви хочете працювати через en.wikipedia.org/wiki/UTF-8 . Це дійсно непогано, і якщо ви зможете зрозуміти все це та інші сторінки Вікіпедії, пов’язані з Unicode, я думаю, ви знайдете, що у вас більше не буде питань.
Боб Мерфі

4
Причина того, що UTF-8 не має проблем з порядком байтів, полягає в тому, що кодування визначається як послідовність байтів і відсутність варіацій з різною витривалістю. Це не має нічого спільного із змінною довжиною.
starblue
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.