Следует ли считать UTF-16 вредным?

432

Я собираюсь спросить, что, вероятно, является довольно спорным вопросом: «Следует ли считать одну из самых популярных кодировок, UTF-16, вредной?»

Почему я задаю этот вопрос?

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

Я знаю; Многие приложения, инфраструктуры и API используют UTF-16, такие как Java String, C # String, Win32 API, библиотеки Qt GUI, библиотека ICU Unicode и т. д. Однако, при всем этом, в обработке есть много основных ошибок символов вне BMP (символы, которые должны быть закодированы с использованием двух элементов UTF-16).

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

  • 𝄞 ( U + 1D11E ) МУЗЫКАЛЬНЫЙ СИМВОЛ G CLEF
  • 𝕥 ( U + 1D565 ) МАТЕМАТИЧЕСКАЯ ДВОЙНАЯ СТРУКТУРА МАЛЫЙ T
  • 𝟶 ( U + 1D7F6 ) МАТЕМАТИЧЕСКИЙ МОНОМЕРНЫЙ ЦИФРОВОЙ НОЛЬ
  • 𠂊 ( U + 2008A ) Хан Персонаж

Вы можете пропустить некоторые, в зависимости от того, какие шрифты вы установили. Все эти персонажи находятся за пределами BMP (базовая многоязычная плоскость). Если вы не видите эти символы, вы также можете попробовать посмотреть их в справочнике символов Unicode .

Например, попробуйте создать имена файлов в Windows, которые включают эти символы; попробуйте удалить эти символы с помощью «backspace», чтобы увидеть, как они ведут себя в разных приложениях, использующих UTF-16. Я сделал несколько тестов, и результаты довольно плохие:

  • Опера имеет проблемы с их редактированием (удалите 2 нажатия на клавишу возврата)
  • Блокнот не может справиться с ними правильно (удалите необходимые 2 нажатия на клавишу возврата)
  • Редактирование имен файлов в диалоговых окнах не работает (необходимо удалить 2 нажатия на клавишу возврата)
  • Все приложения QT3 не могут справиться с ними - показывать два пустых квадрата вместо одного символа.
  • Python неправильно кодирует такие символы при использовании непосредственно u'X'!=unicode('X','utf-16')на некоторых платформах, когда символ X находится за пределами BMP.
  • Unicodedata в Python 2.5 не может получить свойства для таких символов, когда python скомпилирован со строками Unicode UTF-16.
  • Похоже, что StackOverflow удаляет эти символы из текста, если редактируется непосредственно как символы Юникода (эти символы отображаются с использованием экранирования HTML в Юникоде).
  • WinForms TextBox может генерировать недопустимую строку при ограничении MaxLength.

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

Итак ... Как вы думаете, что UTF-16 следует считать вредным?

Артём
источник
64
Не совсем правильно. Я объясняю, если вы пишете «שָׁ» составной символ, который состоит из «ש», «ָ» и «ׁ», vovels, то удаление каждого из них логично, вы удаляете одну кодовую точку, когда нажимаете « Backspace "и удалить все символы, включая гласные, когда нажмите" Del ". Но вы никогда не создаете недопустимое состояние текста - нелегальные кодовые точки. Таким образом, ситуация, когда вы нажимаете клавишу Backspace и получаете незаконный текст, неверна.
41
CiscoIPPhone: Если об ошибке «сообщают несколько раз, много разных людей», а затем пару лет спустя разработчик пишет в блоге разработчиков: «Верьте или нет, поведение в основном преднамеренное!», То (чтобы это мягко) Я склонен думать, что это, вероятно, не лучшее дизайнерское решение, когда-либо принятое. :-) То, что это сделано намеренно, не означает, что это не ошибка.
145
Отличный пост. UTF-16 действительно является «худшим из обоих миров»: UTF8 имеет переменную длину, охватывает весь Unicode, требует алгоритм преобразования в и из необработанных кодовых точек, ограничивает ASCII и не имеет проблем с порядком байтов. UTF32 имеет фиксированную длину, не требует преобразования, но занимает больше места и имеет проблемы с порядком байтов. Пока все хорошо, вы можете использовать UTF32 для внутренних целей и UTF8 для сериализации. Но у UTF16 нет никаких преимуществ: он зависит от порядка байтов, имеет переменную длину, занимает много места, не совместим с ASCII. Усилия, необходимые для правильной работы с UTF16, можно было бы потратить лучше на UTF8.
Kerrek SB
26
@Ian: UTF-8 НЕ имеет таких же предупреждений, как UTF-8. Вы не можете иметь суррогаты в UTF-8. UTF-8 не маскируется под то, что это не так, но большинство программистов, использующих UTF-16, используют его неправильно. Я знаю. Я смотрел их снова и снова и снова и снова.
tchrist
18
Кроме того, UTF-8 не имеет проблемы, потому что все рассматривают его как кодировку переменной ширины. Причина, по которой проблема в UTF-16, заключается в том, что все воспринимают ее как кодирование с фиксированной шириной.
Кристофер Хаммарстрем

Ответы:

340

Это старый ответ.
Смотрите UTF-8 Везде для последних обновлений.

Мнение: Да, UTF-16 следует считать вредным . Сама причина, по которой он существует, заключается в том, что некоторое время назад существовало ошибочное мнение, что widechar будет тем, чем сейчас является UCS-4.

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

С другой стороны, накладные расходы UTF-8 - это небольшая цена, которая имеет значительные преимущества. Преимущества, такие как совместимость с незнакомым кодом, который просто передает строки char*. Это отличная вещь. В UTF-16 есть несколько полезных символов, которые ШОРТЕРнее, чем в UTF-8.

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

Людям, которые говорят « используйте то, что нужно, там, где это необходимо », я говорю: использование везде одинакового кодирования имеет огромное преимущество, и я не вижу достаточных оснований делать иначе. В частности, я думаю, что добавление wchar_tв C ++ было ошибкой, как и добавление Unicode в C ++ 0x. Что требуется от реализаций STL, так это то, что каждый параметр std::stringили char*будет считаться совместимым с юникодом.

Я также против подхода « используй, что хочешь ». Я не вижу причин для такой свободы. Существует достаточно путаницы в предмете текста, в результате чего все это сломанное программное обеспечение. Сказав вышесказанное, я убежден, что программисты должны наконец прийти к консенсусу по UTF-8 как одному правильному пути. (Я родом из не говорящей по-английски страны и вырос на Windows, поэтому в последний раз я должен был атаковать UTF-16 по религиозным мотивам).

Я хотел бы поделиться дополнительной информацией о том, как я делаю текст в Windows, и что я рекомендую всем остальным для проверки правильности юникода во время компиляции, простоты использования и лучшей мультиплатформенности кода. Предложение существенно отличается от того, что обычно рекомендуется в качестве правильного способа использования Unicode на окнах. Тем не менее, углубленное исследование этих рекомендаций привело к тому же выводу. Так что здесь идет:

  • Не используйте и wchar_tни std::wstringв каком другом месте, кроме соседней точки, API-интерфейсы, принимающие UTF-16.
  • Не используйте литералы _T("")или L""UTF-16 (они должны быть исключены из стандарта IMO, как часть устаревания UTF-16).
  • Не используйте типы, функции или их производные, чувствительные к _UNICODEконстанте, такие как LPTSTRили CreateWindow().
  • Тем не менее, _UNICODEвсегда определяется, чтобы избежать передачи char*строк в WinAPI, которые будут автоматически скомпилированы
  • std::stringsи в char*любом месте программы считаются UTF-8 (если не указано иное)
  • Все мои строки std::string, хотя вы можете передать char * или строковый литерал convert(const std::string &).
  • используйте только функции Win32, которые принимают widechars ( 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::wstringwith Utils::Convert:

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

      Придется вручную удалять конвертирование, когда отношение MSVC к fstreamизменениям.

    • Этот код не является мультиплатформенным и может быть изменен вручную в будущем
    • См. fstreamИсследование / обсуждение Unicode, случай 4215 для получения дополнительной информации.
    • Никогда не создавайте текстовые файлы вывода с содержимым не-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
Что я в основном говорю, так это то, что упрощение, обеспечиваемое наличием кодировки One, которая также совместима с существующими программами char *, а также является самой популярной на сегодняшний день для всего, невозможно представить. Это почти как в старые добрые "незашифрованные" дни. Хотите открыть файл с именем? Не нужно беспокоиться о том, что вы делаете с юникодом и т. Д. И т. Д. Я предлагаю разработчикам ограничить UTF-16 особыми случаями серьезной оптимизации, когда крошечная производительность стоит человеко-месяцев работы.
17
При выборе внутреннего использования UTF-8 у Linux было особое требование: совместимость с Unix. Windows не нуждалась в этом, и поэтому, когда разработчики внедрили Unicode, они добавили версии UCS-2 для почти всех функций, обрабатывающих текст, и заставили многобайтовые из них просто конвертировать в UCS-2 и вызывать другие. Позже они заменяют UCS-2 на UTF-16. Linux, с другой стороны, сохранял 8-битное кодирование и, следовательно, использовал UTF-8, так как это правильный выбор в этом случае.
Мирча Кирея
34
@Pavel Radzivilovsky: Кстати, ваши статьи о «Я верю, что все остальные кодировки рано или поздно умрут. Это связано с тем, что MS-Windows, Java, ICU, python перестанут использовать его в качестве своего любимого». и «В частности, я думаю, что добавление wchar_t в C ++ было ошибкой, как и добавление юникода в 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 символов»), вы получите странное и неправильное поведение. Если кодовая точка имеет значение только в сочетании, по крайней мере, с другой, вы не можете справиться с ней самостоятельно любым разумным способом.
Во
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методе класса String ядра Java (также других в строковом классе), которых никогда бы не было, если бы Java использовала UTF-8 или UTF-32. В любом коде, использующем UTF-16, есть миллионы этих спящих бомб, и я устал от них. UTF-16 - порочная оспа, которая навсегда изводит наше программное обеспечение коварными ошибками. Это явно вредно, и его следует осудить и запретить.
tchrist
7
@tchrist Ух ты, не-суррогатная функция (потому что она была написана, когда ее не было, и она печально документирована таким образом, что, вероятно, делает невозможной адаптацию - она ​​указывает .toUpperCase (char)), приведет к неправильному поведению? Вы знаете, что функция UTF-32 с устаревшей картой кодовых точек не справится с этим лучше? Кроме того, весь Java API обрабатывает суррогаты не особенно хорошо, а более сложные моменты, связанные с Unicode, вообще не имеют - и с более поздним использованием используемая кодировка не будет иметь значения вообще.
Во
8
-1: Безусловный .Substring(1)в .NET является тривиальным примером чего-то, что нарушает поддержку всех не-BMP Unicode. Все, что использует 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, независимо от того, что могут делать некоторые платформы).

ninjalj
источник
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), то какой метод кодирования символов будет считаться безопасным?

РЕДАКТИРОВАТЬ: Чтобы уточнить: зачем считать неправильные реализации стандарта отражением качества самого стандарта? Как впоследствии отметили другие, просто потому, что приложение использует инструмент ненадлежащим образом, не означает, что сам инструмент неисправен. Если бы это было так, мы могли бы сказать что-то вроде «ключевое слово var считается вредным» или «потоки считаются вредными». Я думаю, что этот вопрос путает качество и природу стандарта с трудностями, с которыми сталкиваются многие программисты при его правильной реализации и использовании, что, как мне кажется, объясняется их непониманием того, как работает юникод, а не самим юникодом.

patjbs
источник
33
-1: Как насчет решения некоторых из возражений Артема, а не просто покровительствовать ему?
8
КСТАТИ: Когда я начал писать эту статью, я почти хотел написать «Действительно ли Джоэл на Софтире статью Unicode следует считать вредным», потому что есть много ошибок. Например: кодировка utf-8 занимает до 4 символов, а не 6. Кроме того, она не различает UCS-2 и UTF-16, которые действительно отличаются друг от друга - и фактически вызывают проблемы, о которых я говорю.
32
Кроме того, следует отметить, что когда Джоэл писал эту статью, стандарт UTF-8 был 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-битная кодовая точка всегда будет представлять символ. Из-за объединения символов фактический символ может состоять из нескольких кодовых точек. Юникод никогда не бывает тривиальным.

КСТАТИ. Вероятно, существует тот же класс ошибок с платформами и приложениями, которые ожидают, что символы будут 8-битными, которые получают Utf-8.

JacquesB
источник
12
В случае Java, если вы посмотрите на их временную шкалу ( java.com/en/javahistory/timeline.jsp ), вы увидите, что основная разработка String произошла, когда Unicode был 16-битным (он изменился в 1996 году). Им пришлось воспользоваться способностью обрабатывать не кодовые точки BMP, что привело к путанице.
Кэти Ван Стоун
10
@ Кэти: Хотя на самом деле не оправдание C #. В целом, я согласен, что должен быть CodePointтип, содержащий одну кодовую точку (21 бит), CodeUnitтип, содержащий одну кодовую единицу (16 бит для UTF-16), и Characterтип в идеале должен поддерживать полную графему. Но это делает его функционально эквивалентным String...
Joey
1
Этому ответу почти два года, но я не могу не комментировать его. «Наличие типа с именем« char », который не всегда представляет символ, довольно запутанно». И все же люди все время используют его в C и т.п. для представления целочисленных данных, которые можно хранить в одном байте.
JAB
И я видел много кода на C, который неправильно обрабатывает кодировку символов.
Ден04
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. Но, конечно, вам нужно выбрать тот, который наиболее подходит для вашего приложения. Вот почему их больше, чем одного.

rmeador
источник
3
UTF-16 проще для всего внутри BMP, поэтому он так широко используется. Но я тоже фанат UTF-8, у него также нет проблем с порядком байтов, что работает в его пользу.
Малкольм
2
Теоретически да. На практике существуют такие вещи, как, скажем, UTF-16BE, что означает UTF-16 с прямым порядком байтов без спецификации. Это не то, что я придумал, это фактическая кодировка, разрешенная в тегах ID3v2.4 (теги ID3v2 отстой, но, к сожалению, широко используются). И в таких случаях вы должны определить порядок байтов извне, потому что сам текст не содержит спецификации. UTF-8 всегда пишется односторонне, и у него нет такой проблемы.
Малкольм
23
Нет, UTF-16 не проще. Это сложнее. Это вводит в заблуждение и обманывает вас, думая, что это фиксированная ширина. Весь такой код сломан и тем более потому, что вы не замечаете, пока не станет слишком поздно. ПРИМЕР В ПУНКТЕ: Вчера я обнаружил еще одну глупую ошибку UTF-16 в базовых библиотеках Java, на этот раз в String.equalsIgnoreCase, которая была оставлена ​​в ошибке UCS-2 braindeath и поэтому не работает на 16/17 действительных кодовых точках Unicode. Как долго этот код был вокруг? Нет оправдания для того, чтобы глючить. UTF-16 приводит к полной глупости и несчастному случаю, ожидающему случиться. Беги с криком из UTF-16.
tchrist
3
@ tchrist Нужно быть очень невежественным разработчиком, чтобы не знать, что UTF-16 не имеет фиксированной длины. Если вы начнете с Википедии, вы прочтете в самом верху следующее: «Он выдает результат переменной длины, состоящий из одного или двух 16-битных кодовых блоков на кодовую точку». Unicode FAQ говорит то же самое: unicode.org/faq//utf_bom.html#utf16-1 . Я не знаю, как UTF-16 может кого-то обмануть, если везде написано, что это переменная длина. Что касается метода, он никогда не был разработан для UTF-16 и не должен рассматриваться как Unicode, так просто.
Малкольм
2
@tchrist У вас есть источник для вашей статистики? Хотя если хороших программистов мало, я думаю, что это хорошо, потому что мы становимся более ценными. :) Что касается API Java, части на основе символов могут в конечном итоге устареть, но это не является гарантией того, что они не будут использоваться. И они определенно не будут удалены по соображениям совместимости.
Малкольм
18

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

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

Однако я думаю, что проблемы устаревшего программного обеспечения с поддержкой только BMP несколько преувеличены. Персонажи вне BMP встречаются только в очень специфических случаях и областях. Согласно официальному FAQ по Unicode , «даже в восточноазиатском тексте частота суррогатных пар должна составлять в среднем менее 1% от общего объема хранения текста». Конечно, не следует пренебрегать символами вне BMP, так как в противном случае программа не совместима с Юникодом, но большинство программ не предназначены для работы с текстами, содержащими такие символы. Вот почему, если они не поддерживают это, это неприятно, но не катастрофа.

Теперь давайте рассмотрим альтернативу. Если бы UTF-16 не существовало, то у нас не было бы кодировки, которая хорошо подходила бы для текста не-ASCII, и все программное обеспечение, созданное для UCS-2, должно было бы быть полностью переработано, чтобы оставаться 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) не имеет смысла для большинства приложений, так как он требует много места, так что это в значительной степени не стартер.

JasonTrue
источник
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) страдают от проблем с порядком байтов, поэтому их никогда не следует использовать внешне.

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

UTF-16? определенно вредно. Здесь только мое зерно соли, но в программе есть ровно три приемлемых кодировки для текста:

  • ASCII: при работе с вещами низкого уровня (например, микроконтроллерами), которые не могут позволить себе ничего лучше
  • UTF8: хранение на носителях фиксированной ширины, таких как файлы
  • целочисленные кодовые точки («CP»?): массив наибольших целых чисел, которые удобны для вашего языка программирования и платформы (затухает до ASCII в пределе низких коэффициентов сжатия). Должно быть int32 на старых компьютерах и int64 на любом с 64-битной адресацией.

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

Дэвид Х
источник
4
@ Симон Бьюкен, U+10ffffМакс выйдет из окна, когда (не если) они исчерпали кодовые точки. Тем не менее, использование int32 в системе p64 для скорости, вероятно, безопасно, так как я сомневаюсь, что они превысят, U+ffffffffпрежде чем вы будете вынуждены переписать свой код для 128-битных систем около 2050 года. (Это и есть смысл "использовать самый большой int, который удобен », а не« самый большой доступный »(который, вероятно, будет int256 или bignums или что-то в этом роде).
Дэвид X
1
@ Дэвид: Unicode 5.2 кодирует 107 361 кодовых точек. Есть 867 169 неиспользуемых кодов. «когда» просто глупо. Кодовая точка Unicode определяется как число от 0 до 0x10FFFF, свойство, от которого зависит UTF-16. (Кроме того, 2050 год, кажется, сильно занижает оценку для 128-битных систем, когда 64-битная система может удерживать весь Интернет в своем адресном пространстве.)
3
@David: Ваше «когда» было связано с исчерпанием кодовых точек Unicode, а не 128-битного коммутатора, который, да, будет в ближайшие несколько веков. В отличие от памяти, здесь нет экспоненциального роста символов, поэтому Консорциум Unicode специально гарантировал, что они никогда не выделят кодовую точку выше U+10FFFF. Это действительно одна из тех ситуаций , когда 21 бита является достаточно для всех.
10
@ Симон Бьюкен: По крайней мере, до первого контакта. :)
3
Юникод используется для гарантии того, что над U + FFFF также не будет кодовых точек.
Шеннон Северанс
13

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

Utf-16 : охватывает только 1 112 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 охватывает все 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 . Он закодирован с переменной длиной (не может быть доступен по индексу)»
Ян Бойд
9
@ Ян Бойд, необходимость доступа к индивидуальному символу строки в шаблоне произвольного доступа невероятно преувеличена. Это примерно так же часто, как желание вычислить диагональ матрицы символов, что очень редко. Строки практически всегда обрабатываются последовательно, и поскольку доступ к 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. Пользователь вводит текстовый блок или вставляет в него текст.

3. Независимо от того, что вы вводите или вставляете в TextBox, вы ограничены 20, хотя он будет сочувственно подавать звуковой сигнал после текста за 20 (YMMV здесь; я изменил свою звуковую схему, чтобы дать мне этот эффект!).

4. Затем небольшой пакет текста отправляется куда-то еще, чтобы начать захватывающее приключение.

Теперь это простой сценарий, и любой может написать об этом в свободное время. Я сам написал это на нескольких языках программирования, используя WinForms, потому что мне было скучно, и я никогда не пробовал это раньше. И с текстом на нескольких реальных языках, потому что я подключен таким образом, и у меня больше раскладок клавиатуры, чем, возможно, у кого-либо во всей вселенной.

Я даже назвал форму Magic Carpet Ride , чтобы облегчить скуку.

Это не сработало, для чего это стоит.

Вместо этого я ввел следующие 20 символов в форму « Волшебный ковер» :

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

Matthieu
источник
Спасибо, очень хорошая ссылка! Я добавил его в список вопросов в вопросе.
9

Я бы не сказал, что UTF-16 вреден. Это не элегантно, но служит обратной совместимости с UCS-2, так же, как GB18030 с GB2312, а UTF-8 с ASCII.

Но внесение фундаментальных изменений в структуру Unicode в середине потока после того, как Microsoft и Sun создали огромные API-интерфейсы из 16-битных символов, было вредным. Неспособность распространять информацию об изменениях была более вредной.

dan04
источник
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?
Ден04
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, ваш код потерпит неудачу быстро и громко, как только вы попытаетесь интернационализировать.

dsimcha
источник
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.

Предложение:

Возможно, руководство должно содержать пример явного использования широкоформатной версии структуры данных, чтобы упростить ее упускание / забвение. Использование широкоформатных версий структур данных поверх использования широкоформатных версий функций делает еще менее вероятным случайный вызов 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 Конечно, был бы признателен!
Желе Гертс
@JelleGeerts: я прошу прощения за эту задержку. Вы всегда можете связаться с нами по электронной почте (по ссылке из манифеста) или через Facebook. Нас легко найти. Хотя я полагаю, что мы исправили проблему, которую вы привели здесь (и я вам зачитал), все дебаты UTF-8 против UTF-16 по-прежнему актуальны. Если вы хотите внести свой вклад, не стесняйтесь обращаться к нам через эти частные каналы.
ybungalobill
5

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

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

В текущем проекте спецификации C ++ говорится, что UTF-32 и UTF-16 могут иметь варианты с прямым порядком байтов, байтов с прямым порядком байтов и неопределенные варианты. В самом деле? Если бы Unicode указывал, что каждый должен делать little-endian с самого начала, то все было бы проще. (Мне бы тоже было хорошо с big-endian.) Вместо этого, некоторые люди реализовали это одним способом, другие - другим, и теперь мы застряли в глупости впустую. Иногда стыдно быть инженером-программистом.

user22815
источник
Предполагается, что неуказанный endianess включает в себя 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 для всех его ошибок превосходит это. Если бы не было такого неприятного компромисса между недоразумениями разных стран.

Donal Fellows
источник
Зная нашу удачу, через несколько лет в UTF-16 нам не хватит места. Мех.
Donal Fellows
3
Основная проблема заключается в том, что текст обманчиво сложен. Никакой подход к представлению этой информации в цифровой форме не может быть простым. По той же причине, что даты жесткие, календари трудные, время трудное, личные имена трудные, почтовые адреса трудны: всякий раз, когда цифровые машины пересекаются с человеческими культурными конструкциями, возникает сложность. Это факт жизни. Люди не функционируют на цифровой логике.
Аристотель Пагальцис