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

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

3 ответов


что такое wchar_t?

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

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

- C++ [basic.основополагающим] 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 не использует ту же локаль wchar_t в все локали.

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

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

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

варианты

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

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

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

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

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

C++11 добавляет новые виды широких символов в качестве альтернатив wchar_t, char16_t и char32_t с сопутствующими функциями языка / библиотеки. На самом деле это не UTF-16 и UTF-32, но я не думаю, что какая-либо крупная реализация будет использовать что-либо еще. В C++11 также улучшает поддержку UTF-8, например, с строковыми литералами UTF-8, поэтому не нужно будет обманывать VC++ в создании строк в кодировке 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 не соответствует.

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


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

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

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

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

ИМХО...

PS:

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

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


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

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

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

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

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