Что "неправильно" с С++ wchar_t и wstrings? Каковы некоторые альтернативы широким персонажам?

Я видел, как много людей в сообществе С++ (особенно ## С++ на freenode) возмущаются использованием wstrings и wchar_t, и их использование в windows api. Что именно "неправильно" с wchar_t и wstring, и если я хочу поддержать интернационализацию, каковы некоторые альтернативы широким символам?

Ответ 1

Что такое wchar_t?

wchar_t определяется таким образом, что любое языковое кодирование char может быть преобразовано в представление wchar_t, где каждый wchar_t представляет собой ровно один код:

Тип wchar_t - это отдельный тип, значения которого могут представлять собой различные коды для всех членов самого большого расширенного набора символов, указанного среди поддерживаемых локалей (22.3.1).

                                                                              - С++ [basic.fundamental] 3.9.1/5

Это не требует, чтобы wchar_t был достаточно большим, чтобы представлять любой символ из всех локалей одновременно. То есть кодировка, используемая для wchar_t, может различаться между локалями. Это означает, что вы не можете преобразовать строку в wchar_t с помощью одной локали и затем преобразовать обратно в char с помощью другого языкового стандарта. 1

Поскольку использование wchar_t в качестве общего представления между всеми локалями, по-видимому, является основным использованием для wchar_t на практике, вы можете задаться вопросом, для чего это полезно, если не это.

Первоначальное намерение и цель wchar_t заключалось в том, чтобы упростить обработку текста, определяя его таким образом, чтобы он требовал взаимно однозначного сопоставления от строковых кодовых единиц к текстовым символам, что позволяло использовать одни и те же простые алгоритмы которые используются с строками ascii для работы с другими языками.

К сожалению, формулировка спецификации wchar_t предполагает взаимно однозначное сопоставление между символами и кодовыми точками для достижения этого. Unicode нарушает это предположение 2 поэтому вы не можете безопасно использовать wchar_t для простых текстовых алгоритмов.

Это означает, что портативное программное обеспечение не может использовать wchar_t как общее представление для текста между локалями или для использования простых текстовых алгоритмов.

Какое использование wchar_t сегодня?

В любом случае, для портативного кода не так много. Если __STDC_ISO_10646__ определено, то значения wchar_t непосредственно представляют кодовые страницы Unicode с одинаковыми значениями во всех локалях. Это делает безопасным повторение межобластных преобразований, упомянутых ранее. Однако вы не можете полагаться только на это, чтобы решить, что вы можете использовать wchar_t таким образом, потому что, хотя большинство платформ unix определяют его, Windows даже не работает, хотя Windows использует один и тот же локаль wchar_t во всех локалях.

Причина, по которой Windows не определяет __STDC_ISO_10646__, заключается в том, что Windows использует UTF-16 в качестве кодировки wchar_t, а потому, что UTF-16 использует суррогатные пары для представления кодовых точек больше U + FFFF, а это означает, что UTF-16 не использует " t удовлетворяют требованиям для __STDC_ISO_10646__.

Для специфичного для платформы кода wchar_t может быть более полезным. Это по существу требуется для Windows (например, некоторые файлы просто не могут быть открыты без использования имен файлов wchar_t), хотя Windows является единственной платформой, где это правда, насколько я знаю (так что, возможно, мы можем думать о wchar_t как "Windows_char_t" ).

В hindsight wchar_t явно не полезен для упрощения обработки текста или для хранения независимого от языка текста. Портативный код не должен пытаться использовать его для этих целей. Не переносимый код может оказаться полезным только потому, что это требует какой-то API.

Альтернативы

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

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

Одна вещь, которую UTF-8 не предоставляет, - это возможность использовать простые текстовые алгоритмы, например, с ASCII. В этом UTF-8 не хуже любой другой кодировки Unicode. На самом деле это можно считать лучшим, потому что представления нескольких кодовых блоков в UTF-8 более распространены, и поэтому ошибки в обработке кода с такими представлениями ширины переменной ширины чаще отмечаются и фиксируются, чем если вы пытаетесь придерживаться UTF -32 с NFC или NFKC.

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

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

С++ 11 добавляет новые виды широких символов в качестве альтернатив wchar_t, char16_t и char32_t со встроенными функциями языка/библиотеки. На самом деле они не гарантируют UTF-16 и UTF-32, но я не думаю, что какая-либо крупная реализация будет использовать что-либо еще. С++ 11 также улучшает поддержку UTF-8, например, с строковыми литералами UTF-8, поэтому нет необходимости обманывать VС++ для создания кодированных строк UTF-8 (хотя я могу продолжать это делать, а не использовать u8).

Альтернативы, чтобы избежать

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


<Суб > 1. Символы, которые представляются в строках wchar_t, но которые не поддерживаются ни в одной локали, не обязательно должны быть представлены с одним значением wchar_t. Это означает, что wchar_t может использовать кодировку переменной ширины для определенных символов, еще одно явное нарушение намерения wchar_t. Хотя можно утверждать, что символ, представляемый wchar_t, достаточно, чтобы сказать, что локаль "поддерживает" этот символ, и в этом случае кодировки переменной ширины не являются законными, а использование UTF-16 в Window несовместимо.

<суб > 2. Unicode позволяет отображать множество символов с несколькими кодовыми точками, что создает те же проблемы для простых текстовых алгоритмов, что и кодирование переменной ширины. Даже если кто-то строго придерживается сложенной нормализации, некоторым символам по-прежнему требуется несколько кодовых точек. См.: http://www.unicode.org/standard/where/

Ответ 2

Там нет ничего плохого с wchar_t. Проблема в том, что в NT 3.x дни Microsoft решила, что Unicode был хорош (он есть) и реализовать Unicode как 16-битные символы wchar_t. Таким образом, большая часть литературы Microsoft из середины 90-х в значительной степени приравнивается к Unicode == utf16 == wchar_t.

Что, к сожалению, совсем не так. "Широкие символы" не обязательно являются 2 байтами на всех платформах при любых обстоятельствах.

Это один из лучших праймеров на "Unicode" (независимо от этого вопроса, независимо от С++), который я когда-либо видел: я очень рекомендую:

И я искренне верю, что лучший способ справиться с "8-разрядным ASCII" и "широкими буквами Win32" против "wchar_t-in-general" - это просто признать, что "Windows отличается"... и код соответственно.

ИМХО...

PS:

Я полностью согласен с jamesdlin выше:

В Windows у вас действительно нет выбора. Его внутренние API-интерфейсы были разработанный для UCS-2, который был разумным в то время, когда он был до кодирования UTF-8 и UTF-16 переменной длины стандартизированы. Но теперь, когда они поддерживают UTF-16, они оказались худшее из обоих миров.

Ответ 3

Обязательное чтение:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

Если вы программируете на Java или .Net(VB.Net или С#) - это в основном не проблема: оба по умолчанию Unicode. Если вы программируете в "классическом" Win32 API), лучше всего использовать макросы TCHAR и _T() (вместо того, чтобы явно использовать wchar).

Все компиляторы Microsoft VS2005 и более поздние версии, по-моему, по умолчанию 16-бит для C/С++ в любом случае (часть причины, по которой я все еще использую MSVS 6.0 всякий раз, когда могу);

Еще одна хорошая (хотя и несколько устаревшая ссылка):