Чи слід вважати UTF-16 шкідливим?


432

Я збираюся задати, напевно, досить суперечливе запитання: "Чи слід вважати шкідливим одне з найпопулярніших кодувань, UTF-16?"

Чому я задаю це питання?

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

Я знаю; велика кількість програм, фреймворків та API використовують UTF-16, такі як String Java, St # Cring's String, API Win32, бібліотеки Qt GUI, бібліотека Unicode ICU тощо. Однак при всьому цьому існує багато основних помилок при обробці символів поза BMP (символи, які слід закодувати за допомогою двох елементів UTF-16).

Наприклад, спробуйте відредагувати одного з цих символів:

  • 𝄞 ( U + 1D11E ) МУЗИЧНА СИМВОЛ G CLEF
  • 𝕥 ( U + 1D565 ) МАТЕМАТИЧНИЙ ДВОЙНОСТІЙНИЙ МАЛИЙ Т
  • 𝟶 ( U + 1D7F6 ) МАТЕМАТИЧНИЙ МОНОСАП ДИЗИГ ЗЕРО
  • 𠂊 ( U + 2008A ) Хан символів

Ви можете пропустити деякі, залежно від того, які шрифти ви встановили. Ці символи знаходяться поза межами BMP (базової багатомовної площини). Якщо ви не можете бачити цих символів, ви також можете спробувати їх переглянути в довідці про символи Unicode .

Наприклад, спробуйте створити імена файлів у Windows, які включають ці символи; спробуйте видалити цих символів за допомогою "зворотної області", щоб побачити, як вони поводяться в різних програмах, які використовують UTF-16. Я зробив кілька тестів, і результати дуже погані:

  • Opera має проблеми з їх редагуванням (видаліть необхідні 2 натискання на задній простір)
  • Блокнот не може правильно поводитися з ними (видаліть необхідні 2 натискання на задній простір)
  • Редагування імен файлів у діалогових вікнах Вікна (з видаленням потрібно 2 натискання на задній області)
  • Усі програми QT3 не можуть з ними боротися - показати два порожні квадрати замість одного символу.
  • Python неправильно кодує таких символів при використанні безпосередньо u'X'!=unicode('X','utf-16')на деяких платформах, коли X символом поза BMP.
  • Python 2.5 unicodedata не зможе отримати властивості для таких символів, коли python компілюється з рядками Unicode UTF-16.
  • Здається, StackOverflow видаляє цих символів з тексту, якщо їх редагувати безпосередньо як символи Unicode (ці символи відображаються за допомогою втечі HTML Unicode).
  • WinForms TextBox може генерувати недійсну рядок при обмеженні MaxLength.

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

Отже ... Ви вважаєте, що UTF-16 слід вважати шкідливим?


64
Не дуже коректно. Я пояснюю, якщо ви пишете "שָׁ" складений символ, який складається з "ש", "ָ" і "ׁ", голосних, то видалення кожного з них є логічним, ви видаляєте одну кодову точку, коли натискаєте " backspace "та видаліть усі символи, включаючи голосні, коли натисніть" del ". Але ви ніколи не створюєте незаконний стан тексту - незаконні кодові точки. Таким чином, ситуація, коли ви натискаєте зворотний простір і отримуєте незаконний текст, є неправильною.

41
CiscoIPPhone: Якщо про помилку "повідомляється декілька різних разів, різними людьми", а потім через пару років розробник пише в блозі dev, що "Вірите чи ні, поведінка здебільшого навмисна!", Потім (якщо говорити м'яко кажучи) Я схильний вважати, що це, мабуть, не найкраще дизайнерське рішення, яке коли-небудь приймалося. :-) Тільки тому, що це навмисно, не означає, що це не помилка.

145
Чудовий пост. UTF-16 дійсно є "найгіршим з обох світів": UTF8 має змінну довжину, охоплює весь Unicode, вимагає алгоритм перетворення в і з необроблених кодових точок, обмежується ASCII, і у нього немає проблем з витримкою. UTF32 має фіксовану довжину, не потребує перетворень, але займає більше місця та має проблеми з витримкою. На сьогоднішній день добре, ви можете використовувати UTF32 внутрішньо та UTF8 для серіалізації. Але UTF16 не має жодних переваг: це залежно від ендіан, його змінна довжина, займає багато місця, це не сумісно з ASCII. Зусилля, необхідні для належного поводження з UTF16, можуть бути витрачені краще на UTF8.
Керрек СБ

26
@Ian: UTF-8 НЕ має таких самих застережень, що і UTF-8. У UTF-8 ви не можете мати сурогатів. UTF-8 не маскується як щось таке, але більшість програмістів, які використовують UTF-16, використовують його неправильно. Я знаю. Я спостерігав їх знову і знову і знову і знову.
tchrist

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

Відповіді:


340

Це стара відповідь.
Дивіться UTF-8 скрізь для останніх оновлень.

Думка: Так, UTF-16 слід вважати шкідливим . Сама причина його існування полягає в тому, що деякий час тому існувало помилкове переконання, що широкоформатний апарат стане таким, яким зараз є UCS-4.

Незважаючи на "англоцентризм" UTF-8, його слід вважати єдиним корисним кодуванням тексту. Можна стверджувати, що вихідні коди програм, веб-сторінок та XML-файлів, назви файлів ОС та інших текстових інтерфейсів комп'ютер-комп'ютер ніколи не мали існувати. Але коли вони це роблять, текст не лише для людських читачів.

З іншого боку, накладні витрати UTF-8 - це невелика ціна, яку потрібно платити, хоча вона має значні переваги. Такі переваги, як сумісність з необізнаним кодом, який просто передає рядки char*. Це чудова річ. У UTF-16 мало корисних символів, які ШОРТІші, ніж у UTF-8.

Я вірю, що всі інші кодування з часом загинуть. Це означає, що MS-Windows, Java, ICU, python перестають використовувати його як своє улюблене. Після довгих досліджень та обговорень конвенції про розробку в моїй компанії забороняють використовувати UTF-16 в будь-якому місці, крім дзвінків API OS, і це, незважаючи на важливість продуктивності в наших програмах та те, що ми використовуємо Windows. Функції перетворення були розроблені для перетворення завжди припущених UTF8 std::strings в початкові UTF-16, які сама Windows не підтримує належним чином .

Людям, які кажуть " використовувати те, що потрібно там, де це потрібно ", я кажу: є величезна перевага в тому, щоб використовувати одне і те ж кодування скрізь, і я не бачу достатніх причин робити інше. Зокрема, я думаю, що додавання wchar_tдо C ++ було помилкою, а також доповнення Unicode до C ++ 0x. Що потрібно вимагати від реалізації STL, але це те, що кожен std::stringабо char*параметр буде вважатися сумісним з unicode.

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

Мені хотілося б поділитися додатковою інформацією про те, як я створюю текст у Windows, а також що я рекомендую всім іншим для перевірки правильністю унікоду, під час компіляції, простоти використання та кращої багатоплатформованості коду. Пропозиція істотно відрізняється від того, що зазвичай рекомендується як належний спосіб використання Unicode у Windows. Однак поглиблене дослідження цих рекомендацій призвело до того ж висновку. Отже, ось що:

  • Не використовуйте та wchar_tні std::wstringв якому іншому місці, крім сусідньої точки для API, що приймають UTF-16.
  • Не використовуйте _T("")або L""UTF-16 літерали (Ці ІМО повинні бути вилучені зі стандарту, як частина знецінення UTF-16).
  • Не використовуйте типи, функції та їх похідні, які чутливі до _UNICODEконстанти, наприклад, LPTSTRабо CreateWindow().
  • Тим не менш, _UNICODEзавжди визначено, щоб уникнути передачі char*рядків WinAPI беззвучно збирати
  • std::stringsі char*будь-де в програмі вважаються UTF-8 (якщо не сказано інше)
  • Усі мої рядки є std::string, хоча ви можете передати char * або string буквально convert(const std::string &).
  • використовувати лише функції Win32, які приймають широкі версії ( LPWSTR). Ніколи тих, хто приймає LPTSTRабо LPSTR. Передайте параметри таким чином:

    ::SetWindowTextW(Utils::convert(someStdString or "string litteral").c_str())
    

    (У політиці використовуються функції перетворення нижче.)

  • З рядками MFC:

    CString someoneElse; // something that arrived from MFC. Converted as soon as possible, before passing any further away from the API call:
    
    std::string s = str(boost::format("Hello %s\n") % Convert(someoneElse));
    AfxMessageBox(MfcUtils::Convert(s), _T("Error"), MB_OK);
    
  • Робота з файлами, назви файлів і fstream у Windows:

    • Ніколи не передайте аргументи std::stringта const char*імена файлів fstreamродині. MSVC STL не підтримує аргументи UTF-8, але має нестандартне розширення, яке слід використовувати наступним чином:
    • Перетворити std::stringаргументи std::wstringз Utils::Convert:

      std::ifstream ifs(Utils::Convert("hello"),
                        std::ios_base::in |
                        std::ios_base::binary);
      

      Нам доведеться вручну видалити перетворення, коли ставлення MSVC до fstreamзмін.

    • Цей код не є багатоплатформним і його, можливо, доведеться в майбутньому змінити вручну
    • Докладнішу fstreamінформацію див. У справі 4215 щодо unicode.
    • Ніколи не створюйте текстові вихідні файли, що не містять вмісту UTF8
    • Уникайте використання fopen()з RAII / OOD причин. Якщо необхідно, використовуйте _wfopen()та вищезгадані конвенції WinAPI.

// For interface to win32 API functions
std::string convert(const std::wstring& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

std::wstring convert(const std::string& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

// Interface to MFC
std::string convert(const CString &mfcString)
{
#ifdef UNICODE
    return Utils::convert(std::wstring(mfcString.GetString()));
#else
    return mfcString.GetString();   // This branch is deprecated.
#endif
}

CString convert(const std::string &s)
{
#ifdef UNICODE
    return CString(Utils::convert(s).c_str());
#else
    Exceptions::Assert(false, "Unicode policy violation. See W569"); // This branch is deprecated as it does not support unicode
    return s.c_str();   
#endif
}

39
Я не можу погодитися. Переваги utf16 над utf8 для багатьох азіатських мов повністю домінують у ваших пунктах. Наївно сподіватися, що японці, тайці, китайці тощо відмовляться від цього кодування. Проблемними зіткненнями між шаблонами є те, коли графіки в основному здаються схожими, за винятком відмінностей. Я пропоную стандартизувати: фіксований 7bit: iso-irv-170; 8-бітна змінна: utf8; 16-бітна змінна: utf16; 32-бітний фіксований: ucs4.

82
@Charles: дякую за ваш внесок. Щоправда, деякі символи BMP довше в UTF-8, ніж у UTF-16. Але, визнаймося: проблема полягає не в байтах, які займають китайські символи BMP, а в складності розробки програмного забезпечення. Якщо китайському програмісту все-таки доводиться проектувати символи змінної довжини, схоже, що UTF-8 все ще має невелику ціну, яку потрібно заплатити порівняно з іншими змінними в системі. Він може використовувати UTF-16 як алгоритм стиснення, якщо простір настільки важливий, але навіть тоді він не буде відповідати LZ, а після LZ або іншого загального стиснення обидва мають приблизно однаковий розмір і ентропію.

32
Я кажу в основному про те, що спрощення, запропоноване тим, що існує одне кодування, яке також сумісне з існуючими програмами char *, а також є найпопулярнішим сьогодні для всього, що це неможливо. Це майже як у старі добрі "простові" дні. Хочете відкрити файл з ім'ям? Не потрібно дбати про те, який унікод ви робите, тощо. Я пропоную, щоб ми, розробники, обмежили UTF-16 дуже особливими випадками суворої оптимізації, коли крихітна шматочок продуктивності коштує чоловіків-місяців роботи.

17
Linux вирішив використовувати внутрішню UTF-8: сумісність з Unix. Windows цього не потребував, і тому розробники впровадили Unicode, вони додали версії UCS-2 майже всіх функцій, що обробляють текст, і змусили багатобайтові просто перетворитись на UCS-2 та викликати інші. Пізніше вони замінюють UCS-2 на UTF-16. З іншого боку, Linux утримував 8-бітні кодування і, таким чином, використовував UTF-8, оскільки це правильний вибір у такому випадку.
Mircea Chirea

34
@ Павел Радзівіловський: BTW, ваші писання про "Я вірю, що всі інші кодування зрештою помруть. Це передбачає, що MS-Windows, Java, ICU, python перестануть використовувати його як своє улюблене". і "Зокрема, я думаю, що додавання wchar_t до C ++ було помилкою, і так само додавання Unicode до C ++ Ox." або досить наївні, або дуже дуже зарозумілі. І це відбувається від того, хто вдома кодує Linux та хто задоволений символами UTF-8. Якщо говорити прямо: це не відбудеться .
paercebal

157

Кодові точки Unicode - це не символи! Іноді це навіть не гліфи (зорові форми).

Деякі приклади:

  • Римські цифрові кодові точки типу "ⅲ". (Єдиний символ, схожий на "iii".)
  • Наголошені символи типу "á", які можуть бути представлені як єдиний об'єднаний символ "\ u00e1", або символ та розділений діакритичний "\ u0061 \ u0301".
  • Символи на зразок грецької малої сигми, які мають різні форми для середнього ("σ") та кінця ("ς") позицій слів, але які слід вважати синонімами пошуку.
  • Unicode дискретний дефіс U + 00AD, який може бути або не може бути візуально відображений, залежно від контексту, і який ігнорується для семантичного пошуку.

Єдиний спосіб отримати право редагування Unicode - це використовувати бібліотеку, написану експертом , або стати експертом і написати її самостійно. Якщо ви просто рахуєте кодові точки, ви живете в стані гріха.


19
Це. Дуже сильно це. UTF-16 може спричинити проблеми, але навіть використання UTF-32 все-таки може (і буде) надавати вам проблеми.
bcat

11
Що таке персонаж? Ви можете визначити кодову точку як символу і отримати майже все добре. Якщо ви маєте на увазі видимий користувачем гліф, це щось інше.
tchrist

7
@tchrist впевнений, що для виділення місця це визначення добре, але для чого-небудь іншого? Не так багато. Якщо ви обробляєте комбінуючий символ як єдиний символ (тобто для операції видалення або "взяти перших N символів"), ви отримаєте дивну та неправильну поведінку. Якщо точка коду має значення лише в поєднанні хоча б з іншим, ви не можете самостійно впоратися з цим.
Voo

6
@Pacerier, це пізно до вечірки, але я маю це коментувати. Деякі мови мають дуже великі набори потенційних комбінацій діакритики (див. В'єтнамська, тобто mệt đừ). Дуже корисно мати комбінації, а не один символ на діакритик.
asthasr

21
невелика замітка по термінології: кодові дійсно відповідають юнікод символів ; про що Даніел говорить тут - сприймаються користувачем персонажі , які відповідають кластерам графеми унікоду
Крістоф

54

Існує просте правило про те, яку форму перетворення Unicode (UTF) використовувати: - utf-8 для зберігання та комунікації - utf-16 для обробки даних - ви можете перейти з utf-32, якщо більшість використовуваних API платформи використовується utf-32 (поширений у світі UNIX).

Більшість систем сьогодні використовують utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt). Також дивіться цей документ: http://unicode.org/notes/tn12/

Повернувшись до "UTF-16 як шкідливого", я б сказав: точно не.

Люди, які бояться сурогатів (думаючи, що вони перетворять Unicode в кодування змінної довжини), не розуміють інших (набагато більших) складностей, які роблять відображення між символами та кодовою точкою Unicode дуже складним: поєднання символів, лігатур, селекторів варіацій , контрольні символи тощо.

Просто прочитайте цю серію тут http://www.siao2.com/2009/06/29/9800913.aspx і подивіться, як UTF-16 стає легкою проблемою.


26
Додайте кілька прикладів, коли UTF-32 є поширеним у світі UNIX!
maxschlepzig

48
Ні, ви не хочете використовувати UTF-16 для обробки даних. Це біль у попі. Він має всі недоліки UTF-8, але жодного з його переваг. І UTF-8, і UTF-32 явно перевершують злий хак, відомий раніше як пані UTF-16, дівоче прізвище якого було UCS-2.
tchrist

34
Я вчора щойно знайшов помилку в equalsIgnoreCaseметоді ядра Java String класу (також інших в рядковому класі), який ніколи б там не був, якби Java використовувала або UTF-8, або UTF-32. У будь-якому коді, де використовується UTF-16, є мільйони цих спальних бомб, і я їм нудна і втомилася. UTF-16 - це вірна віспа, яка навіює наше програмне забезпечення підступними помилками назавжди і назавжди. Він явно шкідливий, і його слід знехтувати і заборонити.
tchrist

7
@tchrist Ніщо так не знає сурогатної функції (оскільки вона була написана тоді, коли їх не було, і сумно задокументована таким чином, що неможливо адаптуватись - вона вказує .toUpperCase (char)) призведе до неправильної поведінки? Ви знаєте, що функція UTF-32 із застарілою картою кодової точки не допоможе це краще? Крім того, весь Java API не дуже добре обробляє сурогати, і більш хитромудрі моменти щодо Unicode взагалі не є - і з пізніше використовуване кодування взагалі не матиме значення.
Voo

8
-1: Безумовний .Substring(1)у .NET тривіальний приклад того, що порушує підтримку всіх Unicode, що не належить до BMP. Все, що використовує UTF-16, має цю проблему; занадто просто ставитися до цього як до кодування фіксованої ширини, і проблеми ви бачите занадто рідко. Це робить його активно шкідливим для кодування, якщо ви хочете підтримати Unicode.
Роман Старков

43

Так, абсолютно.

Чому? Це пов'язано з використанням коду .

Якщо ви подивитесь на ці статистичні дані про використання кодової точки у великому корпусі Тома Крістіанасена, ви побачите, що коди-точки через 8-бітну BMP використовуються в декількох порядках, якщо їх величина перевищує кодові точки, що не є BMP:

 2663710 U+002013 ‹–›  GC=Pd    EN DASH
 1065594 U+0000A0 ‹ ›  GC=Zs    NO-BREAK SPACE
 1009762 U+0000B1 ‹±›  GC=Sm    PLUS-MINUS SIGN
  784139 U+002212 ‹−›  GC=Sm    MINUS SIGN
  602377 U+002003 ‹ ›  GC=Zs    EM SPACE

 544 U+01D49E ‹𝒞›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL C
 450 U+01D4AF ‹𝒯›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL T
 385 U+01D4AE ‹𝒮›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL S
 292 U+01D49F ‹𝒟›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL D
 285 U+01D4B3 ‹𝒳›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL X

Візьміть точку TDD: "Неперевірений код пошкоджений код", і перефразовуйте його як "необмежений код зламаний код", і подумайте, як часто програмістам доводиться мати справу з кодовими точками, що не належать до BMP.

Помилки, пов'язані з тим, що не мають справи з UTF-16 як кодуванням змінної ширини, набагато частіше залишаються непоміченими, ніж еквівалентні помилки в UTF-8 . Деякі мови програмування все ще не гарантують вам UTF-16 замість UCS-2, а деякі так звані мови програмування високого рівня пропонують доступ до кодових одиниць замість кодових точок (навіть C повинен надавати вам доступ до кодові точки, якщо ви використовуєте wchar_t, незалежно від того, що можуть робити деякі платформи).


16
"Помилки, пов’язані з тим, що не мають справи з UTF-16 як кодуванням змінної ширини, набагато частіше залишаються непоміченими, ніж еквівалентні помилки в UTF-8." Це суть питання, а отже, і правильна відповідь.
Шон Макміллан

3
Точно. Якщо ваше керування UTF-8 буде захищене, це буде відразу очевидно. Якщо ваш UTF-8 обробляється заломленим, ви помітите лише, якщо ви введете незвичайні символи Хана або математичні символи.
Механічний равлик

1
Дуже вірно, але, з іншого боку, для чого одиничні тести, якщо вам слід залежати від удачі, щоб знайти помилок у менш частих випадках?
musiphil

@musiphil: Отже, коли ви востаннє створили одиничний тест для символів, що не належать до BMP?
ніндзя

1
На закінчення мого попереднього твердження: навіть з UTF-8 ви не можете бути впевнені, що ви охопили всі випадки, побачивши лише деякі робочі приклади. Те саме з UTF-16: вам потрібно перевірити, чи працює ваш код як із сурогатами, так і із сурогатами. (Хтось навіть може заперечити, що UTF-8 має щонайменше чотири основні випадки, а UTF-16 лише два.)
musiphil

40

Я б припустив, що мислення UTF-16 може вважатися шкідливим, говорить про те, що вам потрібно краще зрозуміти, що стосується юнікоду .

Оскільки мене не вистачає за те, щоб я висловив свою думку щодо суб'єктивного питання, дозвольте мені детальніше. Що саме вас турбує щодо UTF-16? Ви б хотіли, якби все було закодовано в UTF-8? UTF-7? А як щодо UCS-4? Звичайно, певні програми не розроблені для обробки символу коду вічносинхронних символів, але вони необхідні, особливо в сучасній глобальній інформаційній області, для зв'язку між міжнародними кордонами.

Але насправді, якщо ви вважаєте, що UTF-16 слід вважати шкідливим, оскільки він заплутаний або може бути неправильно реалізований (безумовно, може бути і unicode), то який метод кодування символів вважатиметься нешкідливим?

EDIT: Для уточнення: навіщо вважати неправильне впровадження стандарту відображенням якості самого стандарту? Як згодом зазначали інші, лише те, що програма використовує інструмент неналежним чином, не означає, що сам інструмент є несправним. Якби це було так, ми, ймовірно, могли б сказати такі речі, як "ключове слово var вважається шкідливим" або "нарізка вважається шкідливою". Я думаю, що питання плутає якість та характер стандарту з труднощами, з якими стикаються багато програмістів у його впровадженні та використанні належним чином, що, як мені здається, випливає більше з їхнього нерозуміння того, як працює unicode, а не від unicode себе.


33
-1: Як щодо вирішення деяких заперечень Артема, а не просто опікувань ним?

8
BTW: Коли я почав писати цю статтю, я майже хотів написати «Чи слід Джоела про Softeare статтю Unicode вважати шкідливим», оскільки є багато помилок. Наприклад: кодування utf-8 займає до 4 символів, а не 6. Також воно не відрізняє UCS-2 від UTF-16, які насправді відрізняються - і насправді викликають проблеми, про які я говорю.

32
Також слід зазначити, що коли Джоел писав цю статтю, стандарт UTF-8 WAS був 6 байтами, а не 4. RFC 3629 змінив стандарт на 4 байти кілька місяців ПІСЛЯ він написав цю статтю. Як і більшість у Інтернеті, варто читати з більш ніж одного джерела та знати про вік своїх джерел. Посилання мала на меті не "кінець усіх бути всім", а скоріше відправною точкою.

7
Я вибираю: utf-8 або utf-32, які є: кодування змінної довжини майже у всіх випадках (включаючи BMP) або кодування фіксованої довжини завжди.

18
@iconiK: Не будь дурним. UTF-16 абсолютно не є фактичним стандартом для обробки тексту. Покажіть мені програмування, більше підходить для обробки тексту, що Perl, який завжди (ну, більше десяти років) використовував абстрактні символи з основним представленням UTF-8 всередині. Через це кожна програма Perl автоматично обробляє весь Unicode, не потребуючи того, щоб користувач постійно мавпав навколо ідіотських сурогатів. Довжина рядка - це його кількість у кодових точках, а не одиниці коду. Все інше - це сувора дурість, що ставить назад у сумісність.
tchrist

37

З кодуванням Utf-16 немає нічого поганого. Але мови, які розглядають 16-бітні одиниці як символи, мабуть, вважаються погано розробленими. Мати тип з назвою " char", який не завжди представляє символ, є досить заплутаним. Оскільки більшість розробників очікує, що тип char представляє кодову точку або символ, велика кількість коду, ймовірно, зламається, коли вони потрапляють на символи, пов'язані з BMP.

Однак зауважте, що навіть використання utf-32 не означає, що кожна 32-розрядна кодова точка завжди буде представляти символ. Завдяки поєднанню символів фактичний символ може складатися з декількох кодових точок. Unicode ніколи не є тривіальним.

До речі. Напевно, існує той самий клас помилок з платформами та додатками, які очікують, що символи будуть 8-бітовими, які подаються Utf-8.


12
У випадку Java, якщо ви подивитеся на їх шкалу часу ( java.com/en/javahistory/timeline.jsp ), ви побачите, що головна розробка String відбулася в той час, як Unicode становив 16 біт (змінився в 1996 році). Їм доводилося підкреслювати вміння обробляти точки, що не належать до коду BMP, таким чином, плутанина.
Кеті Ван Стоун

10
@Kathy: Хоча насправді не привід для C #. Як правило, я погоджуюся, що має бути CodePointтип, що містить єдину кодову точку (21 біт), CodeUnitтип, що містить єдину одиницю коду (16 біт для UTF-16) і Characterтип в ідеалі повинен підтримувати повну графему. Але це робить його функціонально еквівалентним String...
Joey

1
Цій відповіді майже два роки, але я не можу не коментувати її. "Мати тип з назвою" char ", який не завжди представляє символ, є досить заплутаним." І все ж люди користуються цим постійно в С і тому подібному для представлення цілих даних, які можуть зберігатися в одному байті.
JAB

І я бачив багато C-коду, який неправильно обробляє кодування символів.
dan04

1
C # має інший привід: він був розроблений для Windows, а Windows побудований на UCS-2 (це дуже прикро, що навіть сьогодні API Windows не можуть підтримувати UTF-8). Плюс, я думаю, що Microsoft хотіла сумісності з Java (.NET 1.0 мала бібліотеку сумісності з Java, але вони дуже швидко відмовилися від підтримки Java - я здогадуюсь, що це пов’язано з позовом Sun проти MS?)
Qwertie

20

Мій особистий вибір - завжди використовувати UTF-8. Це стандарт для Linux майже для всього. Він сумісний із багатьма застарілими програмами. Існує дуже мінімальний накладний витрата з точки зору додаткового простору, який використовується для не латинських символів порівняно з іншими форматами UTF, і є значна економія місця для латинських символів. В Інтернеті латинські мови панують на вищому рівні, і я думаю, що вони стануть в осяжному майбутньому. І щоб вирішити один з головних аргументів у початковому дописі: майже кожен програміст знає, що UTF-8 іноді матиме багатобайтові символи. Не всі мають справу з цим правильно, але вони зазвичай знають, що більше, ніж можна сказати для UTF-16. Але, звичайно, вам потрібно вибрати найбільш відповідний для вашої заявки. Ось чому в першу чергу більше одного.


3
UTF-16 простіший для будь-якого всередині BMP, тому він використовується настільки широко. Але я теж фанат UTF-8, він також не має проблем з порядком байтів, що працює на його користь.
Малькольм

2
Теоретично так. На практиці існують такі речі, як, скажімо, UTF-16BE, що означає UTF-16 у великому ендіані без BOM. Це не щось, що я склав, це фактичне кодування, дозволене в тегах ID3v2.4 (теги ID3v2 смоктають, але, на жаль, широко використовуються). І в таких випадках доводиться визначати витривалість зовні, оскільки сам текст не містить BOM. UTF-8 завжди пишеться в один бік, і в ньому немає такої проблеми.
Малькольм

23
Ні, UTF-16 не простіший. Це важче. Це вводить в оману і обманює вас на думку, що це фіксована ширина. Весь такий код порушений, і тим більше, що ви не помічаєте, поки не пізно. СЛУЧАЙ В ТОЧКІ: Я щойно знайшов чергову дурну помилку UTF-16 у основних бібліотеках Java, на цей раз у String.equalsIgnoreCase, яка залишилася в помилках UCS-2, що не відповідає мозковій помилці. Скільки часу існує цей код? Немає приводу, щоб це було баггі. UTF-16 призводить до глупості і до аварії, яка чекає цього. Вибігайте криком від UTF-16.
tchrist

3
@tchrist Треба бути дуже неосвіченим розробником, щоб не знати, що UTF-16 не має фіксованої довжини. Якщо ви почнете з Вікіпедії, ви прочитаєте наступне в самому верху: "Це створює результат змінної довжини або однієї або двох 16-бітних одиниць коду на кодову точку". Поширені питання Unicode говорить те саме: unicode.org/faq//utf_bom.html#utf16-1 . Я не знаю, як UTF-16 може когось обдурити, якщо скрізь написано, що він змінної довжини. Щодо методу, він ніколи не був розроблений для UTF-16 і не повинен вважатися Unicode таким простим.
Малькольм

2
@tchrist У вас є джерело вашої статистики? Хоча якщо хороших програмістів мало, я думаю, що це добре, тому що ми стаємо більш цінними. :) Що стосується API Java, деталі на основі char можуть згодом застаріти, але це не є гарантією того, що вони не будуть використані. І вони точно не будуть видалені з міркувань сумісності.
Малькольм

18

Ну, є кодування, яке використовує символи фіксованого розміру. Я звичайно маю на увазі UTF-32. Але 4 байти на кожен символ - це занадто багато витраченого простору, чому б ми використовували його в повсякденних ситуаціях?

На мій погляд, більшість проблем виникає через те, що деяке програмне забезпечення відстало від стандарту Unicode, але не вдалося виправити ситуацію. Opera, Windows, Python, Qt - всі вони з'явилися до того, як UTF-16 став широко відомим або навіть з'явився на світ. Я можу підтвердити, що в Opera, Windows Explorer та Notepad більше немає проблем з персонажами поза BMP (принаймні, на моєму ПК). Але в будь-якому випадку, якщо програми не розпізнають сурогатних пар, вони не використовують UTF-16. Які б проблеми не виникали при роботі з такими програмами, вони не мають нічого спільного з самим UTF-16.

Однак я вважаю, що проблеми застарілого програмного забезпечення лише з підтримкою BMP дещо перебільшені. Персонажі поза BMP зустрічаються лише у дуже конкретних випадках та областях. Згідно з офіційним поширеним запитанням Unicode , "навіть у тексті східної Азії частота пар сурогатних пар повинна в середньому бути значно меншою за 1% від усіх сховищ тексту". Звичайно, символами поза BMP не слід нехтувати, оскільки програма не відповідає Unicode, інакше, але більшість програм не призначені для роботи з текстами, що містять такі символи. Ось чому, якщо вони цього не підтримують, це неприємно, але не катастрофа.

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

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

Тому я не думаю, що саме кодування слід вважати шкідливим. UTF-16 - це компроміс між простотою та компактністю, і немає ніякої шкоди у використанні того, що потрібно там, де це потрібно . У деяких випадках вам потрібно залишатися сумісним з ASCII, і вам потрібен UTF-8, в деяких випадках ви хочете працювати з роботою з ідеографами Хана і заощаджувати простір за допомогою UTF-16, в деяких випадках вам потрібні універсальні представлення символів, які призначають фіксовану- кодування довжини Використовуйте те, що є більш підходящим, просто зробіть це правильно.


21
Це досить блимаючий, англоцентричний погляд, Малькольм. Майже нарівні з "ASCII достатньо хороший для США - решта світу повинна відповідати нам".
Джонатан Леффлер

28
Насправді я з Росії і постійно стикаюся з кирилицею (включаючи власні програми), тому не думаю, що в мене є англоцентричний погляд. :) Згадування про ASCII не зовсім підходить, оскільки це не Unicode і не підтримує конкретних символів. UTF-8, UTF-16, UTF-32 підтримують ті самі міжнародні набори символів, вони просто призначені для використання у своїх конкретних областях. І це якраз моя думка: якщо ви в основному використовуєте англійську, використовуйте UTF-8, якщо ви в основному використовуєте кирилицю, використовуйте UTF-16, якщо ви використовуєте давні мови, використовуйте UTF-32. Досить просто.
Малькольм

16
"Неправда, азіатські сценарії, такі як японська, китайська або арабська, також належать до BMP. Сам BMP насправді дуже великий і, безумовно, достатньо великий, щоб включити всі сценарії, які використовуються сьогодні" Це все так неправильно. BMP містить 0xFFFF символів (65536). У китайців тільки більше. Китайські стандарти (GB 18030) мають більше. Unicode 5.1 вже виділив більше 100 000 символів.

12
@Marcolm: "Сам BMP насправді дуже великий і, безумовно, достатньо великий, щоб включати всі сценарії, які використовуються сьогодні" Неправда. На даний момент Unicode вже виділив близько 100 К символів, набагато більше, ніж BMP. За межами BMP є великі шматки китайських символів. А деякі з них вимагаються GB-18030 (обов'язковий китайський стандарт). Інші необхідні за (необов’язковими) японськими та корейськими стандартами. Тож якщо ви намагаєтесь продати що-небудь на цих ринках, вам потрібна підтримка BMP.

8
Все, що використовує UTF-16, але може обробляти лише вузькі символи BMP, насправді не використовує UTF-16. Він баггі і зламаний. Передумова ОП - це здорово: UTF-16 шкідливий, оскільки призводить наївних людей до написання зламаного коду. Або ви можете обробити текст Unicode, або ви не можете. Якщо ви не можете, то вибираєте підмножину, яка так само дурна, як і обробка тексту лише для ASCII.
tchrist

16

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

Зростання стрибка до UTF-16 значно покращило адекватність середньої продукції, що обробляє міжнародний текст. Є лише кілька вузьких випадків, коли сурогатні пари потрібно розглядати (вилучення, вставки та розриви рядків, в основному), а середній випадок - це переважно прямий прохід. І на відміну від більш ранніх кодувань, таких як варіанти JIS, UTF-16 обмежує сурогатні пари в дуже вузькому діапазоні, тому перевірка дійсно швидка і працює вперед і назад.

Зрозуміло, це приблизно так само швидко і в правильно закодованому UTF-8. Але також є багато зламаних програм UTF-8, які неправильно кодують сурогатні пари як дві послідовності UTF-8. Тож UTF-8 також не гарантує порятунку.

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

UTF-32 (він же UCS4) є безглуздим для більшості застосунків, оскільки він настільки вимогливий до простору, тому він є майже нестандартним.


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

11
Про що говорить Джейсон, - це програмне забезпечення, яке свідомо реалізує UTF-8 таким чином: створити сурогатну пару, а потім UTF-8 кодувати кожну половину окремо. Правильна назва цього кодування - CESU-8, але Oracle (наприклад) неправильно представляє його як UTF-8. У Java використовується аналогічна схема серіалізації об'єктів, але вона чітко зафіксована як "Модифікований UTF-8" і лише для внутрішнього використання. (Тепер, якби ми могли просто змусити людей читати цю документацію та перестати використовувати DataInputStream # readUTF () та DataOutputStream # writeUTF () недоречно ...)

AFAIK, UTF-32 як і раніше кодує змінну довжину і не дорівнює UCS4, що є специфічним діапазоном кодової точки.
Eonil

@Eonil, UTF-32 завжди відрізнятиметься від UCS4, лише якщо у нас є стандарт Unicode, який містить щось на зразок UCS5 або більше.
JasonTrue

@JasonTrue Тим не менш, лише результати збігаються однаково, не гарантуючи дизайн. Те саме відбулося в 32-бітовій адресації пам'яті, Y2K, UTF16 / UCS2. Або ми маємо якусь гарантію такої рівності? Якщо у нас є, я б із задоволенням користувався цим. Але я не хочу писати можливий код, який можна зламати . Я пишу код рівня символів, і відсутність гарантованого способу перекодування між кодовою точкою UTF <-> дуже сильно клопоче мене.
Eonil

16

UTF-8, безумовно, може пройти, можливо, супроводжується UTF-32 для внутрішнього використання в алгоритмах, які потребують високопродуктивного випадкового доступу (але ігнорує комбінування символів).

Як UTF-16, так і UTF-32 (а також їх варіанти LE / BE) страждають від проблем з ендіазністю, тому їх ніколи не слід застосовувати зовнішньо.


9
Постійний доступ до довільного часу можливий і для UTF-8, просто використовуйте кодові одиниці, а не кодові точки. Можливо, вам потрібен реальний доступ у випадковій кодовій точці, але я ніколи не бачив випадків використання, і ви настільки ж хочете замість цього отримати доступ до випадкових кластерних графемів.

15

UTF-16? безумовно шкідливий. Тут просто моє зерно солі, але в програмі є рівно три прийнятних коду для тексту:

  • ASCII: при роботі з речами низького рівня (наприклад, мікроконтролерами), які не можуть дозволити собі нічого кращого
  • UTF8: зберігання на носіях фіксованої ширини, таких як файли
  • цілі кодові точки ("CP"?): масив найбільших цілих чисел, зручних для вашої мови програмування та платформи (розпадається на ASCII в межах малих курортів). Повинно бути int32 на старих комп'ютерах і int64 на будь-якому, що має 64-бітну адресацію.

  • Очевидно, що інтерфейси застарілого коду використовують те, що кодування потрібно для того, щоб старий код працював правильно.


4
@simon buchan, U+10ffffмаксимум вийде у вікно, коли (якщо не) у них закінчуються кодові точки. Однак, використання int32 в системі p64 для швидкості, ймовірно, безпечно, тому що я сумніваюся, що вони перевищуватимуть U+ffffffffвас, перш ніж ви змушені переписати свій код для 128-бітових систем близько 2050 року. (Це суть "використання найбільшого int що зручно "на відміну від" найбільшого доступного "(що, мабуть, буде int256 або bignums чи щось таке).
David X

1
@David: Unicode 5.2 кодує 107 361 точку коду. Є 867 169 невикористаних кодових точок. "коли" просто нерозумно. Кодова точка Unicode визначається як число від 0 до 0x10FFFF, властивість якого залежить від UTF-16. (Також 2050 здається значно низькою оцінкою для 128-бітних систем, коли 64-розрядна система може вмістити всю Інтернет в своєму адресному просторі.)

3
@David: Ваш "коли" мав на увазі відсутність кодових точок Unicode, а не 128-бітний комутатор, який, так, буде в найближчі кілька століть. На відміну від пам’яті, експоненціального зростання символів немає, тому консорціум Unicode спеціально гарантував, що вони ніколи не виділять кодову точку вище U+10FFFF. Це дійсно одна з тих ситуацій , коли 21 біта є досить для всіх.

10
@Simon Buchan: Принаймні до першого контакту. :)

3
Unicode використовується для гарантії, що над U + FFFF також не буде кодових точок.
Шеннон Северанс

13

Unicode визначає кодові точки до 0x10FFFF (1,114,112 кодів), всі програми, що працюють у багатомовній середовищі, що мають рядки / назви файлів тощо, повинні правильно поводитися з цим.

Utf-16 : охоплює лише 1112,064 коди. Хоча ті, що знаходяться в кінці Unicode, є із літаків 15-16 (зона приватного користування). Він не може більше зростати в майбутньому, окрім порушення концепції Utf-16 .

Utf-8 : теоретично охоплює 2,216,757,376 кодів. Поточний діапазон кодів Unicode може бути представлений максимально 4-байтною послідовністю. Він не страждає від проблеми байт-порядку , він "сумісний" з ascii.

Utf-32 : теоретично охоплює 2 ^ 32 = 4,294,967,296 кодів. Наразі вона не кодується змінної довжини і, мабуть, не буде в майбутньому.

Ці факти самі пояснюють. Я не розумію, щоб виступати за загальне використання Utf-16 . Він кодований змінної довжини (не можна отримати доступ до індексу), він має проблеми з охопленням всього діапазону Unicode навіть в даний час, порядок байтів повинен оброблятися і т. Д. Я не бачу жодної переваги, окрім того, що він використовується в Windows і деяких інші місця. Навіть незважаючи на те, що під час написання багатоплатформового коду, можливо, краще використовувати Utf-8 на власному рівні та здійснювати перетворення лише в кінцевих точках залежно від платформи (як уже пропонується). Коли необхідний прямий доступ за індексом і пам'ять не є проблемою, слід використовувати Utf-32 .

Основна проблема полягає в тому, що багато програмістів, що працюють з Windows Unicode = Utf-16, навіть не знають і не ігнорують той факт, що він кодується змінної довжини.

Те, як це зазвичай в * nix платформі, досить добре, c рядки (char *) інтерпретуються як закодовані Utf-8 , широкі c рядки (wchar_t *) інтерпретуються як Utf-32 .


7
Примітка: UTF-16 охоплює All Unicode, оскільки консорціум Unicode вирішив, що 10FFFF - це ТОП-діапазон Unicode і визначив максимальну довжину 4 байтів UTF-8 і явно виключив діапазон 0xD800-0xDFFF з допустимого діапазону кодових точок, і цей діапазон використовується для створення сурогатні пари. Отже, будь-який дійсний текст Unicode може бути представлений з кожним із цих кодувань. Також про зростання в майбутнє. Не здається, що 1 мільйон кодових очок не вистачить у жодному майбутньому.

7
@Kerrek: Неправильно: UCS-2 не є дійсним кодуванням Unicode. Всі кодування UTF- * за визначенням можуть представляти будь-яку точку коду Unicode, яка є законною для обміну. UCS-2 може представляти набагато менше, ніж ще декілька. Повторімо: UCS-2 не є дійсним кодуванням Unicode, більше ніж ASCII.
tchrist

1
"Я не розумію, що виступає за загальне використання Utf-8 . Він кодується змінної довжини (не можна отримати доступ до індексу)"
Ian Boyd,

9
@Ian Boyd, необхідність доступу до індивідуального символу рядка у шаблоні випадкового доступу неймовірно завищена. Це приблизно так само часто, як і бажати обчислити діагональ матриці символів, що дуже рідко. Рядки практично завжди обробляються послідовно, і оскільки доступ до UTF-8 char N + 1 з огляду на те, що ви перебуваєте на UTF-8 char N - це O (1), проблем не виникає. Надзвичайно мало необхідності робити випадковий доступ до рядків. Чи вважаєте ви, що варто пам’яті перейти до UTF-32 замість UTF-8 - це ваша власна думка, але для мене це взагалі не проблема.
tchrist

2
@tchrist, я надаю вам рядки практично завжди обробляються послідовно, якщо ви включаєте зворотну ітерацію як "послідовну" і розтягую, що трохи далі порівняння кінцевого кінця рядка з відомим рядком. Два дуже поширених сценарії - це обрізка пробілів з кінця рядків і перевірка розширення файлу в кінці шляху.
Енді Дент

11

Додайте це до списку:

Представлений сценарій простий (ще простіший, оскільки я його тут представлю, ніж це було спочатку!): 1. WinForms TextBox сидить у формі, порожній. У нього встановлений MaxLength на 20 .

2. Користувач вводить у TextBox або, можливо, вставляє текст у нього.

3. Незалежно від того, що ви вводите або вставляєте в TextBox, вам обмежено 20, хоча це буде співчутливо подавати звуковий сигнал на текст поза 20 (YMMV тут; я змінив звукову схему, щоб надати мені такий ефект!).

4. Маленький пакет тексту надсилається кудись інше, щоб розпочати захоплюючу пригоду.

Зараз це простий сценарій, і кожен може написати це у вільний час. Я просто написав це на декількох мовах програмування за допомогою WinForms, тому що мені було нудно і ніколи раніше не пробував. І з текстом на декількох власних мовах, тому що я провідний таким чином і маю більше розкладок на клавіатурі, ніж, можливо, хтось у всьому вигадливому Всесвіті.

Я навіть назвав форму Magic Carpet Ride , щоб допомогти покращити нудьгу.

Це не вийшло, для чого це варто.

Тому замість цього я вписав наступні 20 символів у свою форму Magic Carpet Ride :

0123401234012340123 𠀀

Ой-ой.

Останнім персонажем є U + 20000, перший ідеограф розширення B Unicode (він же U + d840 U + dc00, його близьким друзям, яких він не соромиться відхиляти, як би перед ним) ....

введіть тут опис зображення

А зараз у нас гра з м'ячем.

Бо коли TextBox.MaxLength говорить про

Отримує або встановлює максимальну кількість символів, які можна ввести вручну в текстове поле.

що це насправді означає

Отримує або встановлює максимальну кількість одиниць коду UTF-16 LE, які можна вручну ввести у текстове поле і безжально вирізати живу лайну з будь-якого рядка, який намагається грати в люб'язні ігри з мовним символом, що лише хтось одержимий як що Каплан знайде образливим (боже, йому потрібно вийти більше!).

Я спробую ознайомитись із оновленням документа ....
Регулярні читачі, які пам’ятають мою серію UCS-2 до UTF-16 , відзначать моє нещастя спрощеним поняттям TextBox.MaxLength і тим, як він повинен працювати як мінімум у цьому випадку там, де його драконівська поведінка створює протизаконну послідовність, та, яку можуть відкинути інші частини .Net Framework

  • System.Text.EncoderFallbackException: Неможливо перевести символ Unicode \ uD850 в індексі 0 на вказану кодову сторінку. *

виняток, якщо ви передаєте цей рядок деінде в .Net Framework (як це робив мій колега Ден Томпсон).

Тепер добре, можливо, повну серію UCS-2 до UTF-16 багато хто недосяжний.
Але чи не розумно очікувати, що TextBox.Text не створить System.Stringце не спричинить закидання ще однієї частини .Net Framework? Я маю на увазі, це не так, як є шанс у вигляді якоїсь події на елементі управління, яка повідомляє про майбутнє скорочення, де ви можете легко додати розумнішу перевірку - перевірку, яку сам контроль не проти зробити. Я б сказав, що цей панк-контроль порушує договір безпеки, що навіть може призвести до проблем із безпекою, якщо ви зможете спричинити несподівані винятки для припинення роботи програми як грубий вид відмови у наданні послуги. Чому будь-який процес чи метод, алгоритм чи методика WinForms повинні давати недійсні результати?

Джерело: Блог Майкла С. Каплана MSDN


Дякую, дуже гарне посилання! Я додав його до списку питань у питанні.

9

Я б не обов'язково говорив, що UTF-16 є шкідливим. Це не елегантно, але він виконує свою мету зворотної сумісності з UCS-2, як GB18030 і GB2312, а UTF-8 - з ASCII.

Але внести кардинальні зміни в структуру Unicode в середині потоку, після того як Microsoft і Sun створили величезні API з 16-бітових символів, було шкідливим. Неспроможність поширити обізнаність про зміни була більш шкідливою.


8
UTF-8 - це супернабір ASCII, але UTF-16 НЕ є супернабором UCS-2. Хоча майже суперсеть, правильне кодування UCS-2 в UTF-8 призводить до гидоти, відомої як CESU-8; UCS-2 не має сурогатів, а лише звичайні кодові пункти, тому вони повинні бути переведені як такі. Справжня перевага UTF-16 полягає в тому, що оновити кодову базу UCS-2 простіше, ніж повністю переписати для UTF-8. Смішно, так?

1
Звичайно, технічно UTF-16 не є суперкомплект UCS-2, але коли U + D800 до U + DFFF коли-небудь використовувались для чогось, крім сурогатів UTF-16?
dan04

2
Не має значення. Будь-яка обробка, окрім сліпого проходження через бітестрім, вимагає розшифрувати сурогатні пари, чого ви не можете зробити, якщо ви трактуєте це як UCS-2.

6

UTF-16 - найкращий компроміс між обробкою та простором, і тому більшість основних платформ (Win32, Java, .NET) використовують його для внутрішнього представлення рядків.


31
-1 тому, що UTF-8, швидше за все, буде меншим або суттєво не відрізнятиметься. Для деяких азіатських сценаріїв UTF-8 - три байти на гліф, тоді як UTF-16 - лише два, але це врівноважено тим, що UTF-8 є лише одним байтом для ASCII (що часто з’являється навіть у азіатських мовах у назвах продуктів, командах тощо) речі). Крім того, в цих мовах гліф передає більше інформації, ніж латинський символ, тому виправдано, що він займе більше місця.

32
Я б не назвав поєднання гірших сторін обох варіантів хорошим компромісом.

18
Це не простіше, ніж UTF-8. Він також змінної довжини.
luiscubal

36
Залишаючи суперечки про переваги UTF-16: Те, що ви цитували, не є причиною для Windows, Java або .NET, що використовує UTF-16. Windows та Java датуються часом, коли Unicode був 16-бітним кодуванням. UCS-2 тоді був розумним вибором. Коли Unicode став 21-бітним кодуванням, що мігрує до UTF-16, був найкращим вибором, який мали існуючі платформи. Це не мало нічого спільного з простотою управління та космічними компромісами. Це лише питання спадщини.
Джої

10
.NET успадковує спадщину Windows тут.
Джої

6

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


6

Оскільки я поки не можу коментувати, я публікую це як відповідь, оскільки, схоже, інакше не можу зв’язатися з авторами utf8everywhere.org. Прикро, що я автоматично не отримую привілею для коментарів, оскільки маю достатньо репутації на інших змінах ставок.

Це розуміється як коментар до висновку: Так, UTF-16 слід вважати шкідливою відповіддю.

Одне невелике виправлення:

Щоб не допустити випадкового передачі UTF-8 char*у ANSI-рядкові версії функцій Windows-API, слід визначити UNICODE, не _UNICODE. _UNICODEкарти функція , як _tcslenдо wcslen, а НЕ MessageBoxдо MessageBoxW. Натомість UNICODEдефініція піклується про останнє. На доказ, це з WinUser.hзаголовка MS Visual Studio 2005 :

#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

Як мінімум, цю помилку слід виправити utf8everywhere.org.

Пропозиція:

Можливо, посібник повинен містити приклад явного використання широкопоточної версії структури даних, щоб зробити його менш легким пропустити / забути. Використання Wide-string версій структур даних, крім використання Wide-string версій функцій, робить ще меншою ймовірність того, що хтось випадково викликає ANSI-рядкову версію такої функції.

Приклад прикладу:

WIN32_FIND_DATAW data; // Note the W at the end.
HANDLE hSearch = FindFirstFileW(widen("*.txt").c_str(), &data);
if (hSearch != INVALID_HANDLE_VALUE)
{
    FindClose(hSearch);
    MessageBoxW(nullptr, data.cFileName, nullptr, MB_OK);
}

Домовились; Дякую! Ми оновимо документ. Документ ще потребує розробки та додавання інформації про бази даних. Ми раді отримувати внески з формулюванням.
Павло Радзивиловський

@PavelRadzivilovsky _UNICODEвсе ще є :(
cubuspl42

дякую за нагадування. cubus, Jelle, Бажаєте користувача нашого SVN?
Павло Радзівіловський

@Pavel Звичайно, оцінив би це!
Jelle Geerts

@JelleGeerts: Прошу вибачення за цю затримку. Ви завжди можете зв’язатися з нами за нашими електронними листами (пов’язаними з маніфесту) або Facebook. Нас легко знайти. Хоча я вважаю, що ми виправили проблему, яку ви привели сюди (і я вам там зарахував), цілі дебати UTF-8 проти UTF-16 все ще актуальні. Якщо у вас є більше коштів, не соромтесь зв’язатися з нами через ці приватні канали.
ybungalobill

5

Хтось сказав, що UCS4 та UTF-32 були однаковими. Не так, але я знаю, що ти маєш на увазі. Однак одна з них - це кодування іншої. Я б хотів, щоб вони думали вказати витривалість з першого, щоб у нас не було битви за ендіазність і тут. Хіба вони не бачили, що це прийде? Принаймні, UTF-8 скрізь однаковий (якщо тільки хтось не дотримується оригінальної специфікації з 6-байтовими).

Якщо ви використовуєте UTF-16, вам потрібно включити обробку для багатобайтових символів. Ви не можете перейти до N-го символу, індексуючи 2N в байтовий масив. Ви повинні ходити по ньому або мати показники символів. Інакше ви написали помилку.

Поточна специфікація проекту C ++ говорить про те, що UTF-32 та UTF-16 можуть мати варіанти малої ендіанської, великої ендіанської та не визначеної. Дійсно? Якби Unicode вказав, що всі повинні були робити мало ендіан з самого початку, тоді все було б простіше. (Мені б добре було і з біг-ендіаном.) Натомість, деякі люди реалізували це в один бік, а в інші - і тепер ми зациклювались ні на що. Іноді соромно бути інженером програмного забезпечення.


Невизначена цілеспрямованість повинна містити BOM як перший символ, який використовується для визначення того, яким способом слід читати рядок. UCS-4 і UTF-32 справді є одними і тими ж, тобто числове значення UCS між 0 і 0x10FFFF, що зберігається в 32-бітовому цілому.

5
@Tronic: Технічно це неправда. Хоча UCS-4 може зберігати будь-яке 32-бітове ціле число, UTF-32 забороняється зберігати не символьні кодові точки, які є незаконними для обміну, наприклад 0xFFFF, 0xFFFE, і всі сурогати. UTF - транспортне кодування, а не внутрішнє.
tchrist

Проблеми з ендіансністю неминучі, доки різні процесори продовжують використовувати різні замовлення байтів. Однак, можливо, було б непогано, якби був "бажаний" порядок байт для зберігання файлів UTF-16.
Qwertie

Хоча UTF-32 має фіксовану ширину для кодових точок , він не є фіксованою шириною для символів . (Чули про щось, що називається "поєднання символів"?) Тож ви не можете перейти до N-го символу, просто індексуючи 4N в байтовий масив.
musiphil

2

Я не думаю, що це шкідливо, якщо розробник буде досить обережним.
І вони повинні прийняти цю торгівлю, якщо вони теж добре знають.

Як японський розробник програмного забезпечення, я вважаю, що UCS-2 досить великий, а обмеження простору, очевидно, спрощує логіку та зменшує обсяг пам’яті, тому використання utf-16 під обмеженням UCS-2 досить добре.

Існує файлова система чи інша програма, яка передбачає, що кодові точки та байти пропорційні, так що гарантовано, що номер необмеженої кодової точки може бути придатним до певного сховища фіксованого розміру.

Одним із прикладів є NTFS та VFAT із зазначенням UCS-2 як кодування для зберігання імен файлів.

Якщо цей приклад дійсно хоче поширити на підтримку UCS-4, я б погодився використовувати utf-8 для всього, але все-таки фіксована довжина має хороші моменти, такі як:

  1. може гарантувати розмір за довжиною (розмір даних та довжина кодової точки пропорційний)
  2. може використовувати номер кодування для пошуку хешу
  3. нестиснені дані мають достатній розмір (порівняно з utf-32 / UCS-4)

У майбутньому, коли потужність пам’яті / обробки дешева навіть у будь-яких вбудованих пристроях, ми можемо прийняти, що пристрій дещо повільний для додаткових пропусків кешу або помилок сторінки та додаткового використання пам’яті, але, мабуть, це станеться найближчим часом…


3
Для тих, хто читає цей коментар, варто зазначити, що UCS-2 - це не те саме, що UTF-16. Будь ласка, знайдіть відмінності, щоб зрозуміти.
mikebabcock

1

"Чи слід вважати шкідливим одне з найпопулярніших кодувань, UTF-16?"

Цілком можливо, але альтернативи не слід розглядати як набагато кращі.

Фундаментальне питання полягає в тому, що існує багато різних понять про: гліфи, символи, кодові точки та послідовності байтів. Відображення між ними нетривіальним, навіть за допомогою бібліотеки нормалізації. (Наприклад, деякі символи європейських мов, написані на латинській скрипті, не записуються з єдиною кодовою точкою Unicode. І це в більш простому кінці складності!) Це означає, що правильно все виправдати досить дивовижно важкий; слід очікувати химерних помилок (і замість того, щоб просто стогнати про них тут, повідомте керівникам відповідного програмного забезпечення).

Єдиний спосіб, коли UTF-16 можна вважати шкідливим на відміну, скажімо, від UTF-8, це те, що він має інший спосіб кодування точок коду поза BMP (як пара сурогатів). Якщо код бажає отримати доступ або повторити його по кодовій точці, це означає, що він повинен знати про різницю. Щодо OTOH, це означає, що значна частина існуючого коду, що передбачає "символи", завжди може бути вписана в двобайтову кількість - досить поширене, якщо неправильне припущення - може принаймні продовжувати працювати, не будуючи це все. Іншими словами, принаймні ви побачите тих персонажів, з якими не керується правильно!

Я б повернув ваше запитання на голову і сказав, що весь проклятий шебанг Unicode слід вважати шкідливим, і кожен повинен використовувати 8-бітове кодування, за винятком я бачив (за останні 20 років), куди це призводить: жахливо плутанина через різні кодування ISO 8859, плюс весь набір з них, що використовуються для кирилиці, і комплект EBCDIC, і ... ну, Unicode за всі свої помилки перемагає це. Якби це був не такий неприємний компроміс між непорозуміннями різних країн.


Знаючи нашу удачу, через кілька років ми виявимо, що у UTF-16 не вистачає місця. Мех.
Дональні стипендіати

3
Основоположним питанням є те, що текст обманливо важкий. Жоден підхід до представлення цієї інформації в цифровому вигляді не може бути складним. Це та сама причина, що дати важкі, календарі важкі, важкий час, особисті імена важкі, поштові адреси важкі: щоразу, коли цифрові машини перетинаються з людськими культурними конструкціями, складність вибухає. Це факт життя. Люди не функціонують на цифровій логіці.
Арістотель Пагалціс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.