Що “не так” у C ++ wchar_t та wstrings? Які альтернативи широким персонажам?


86

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


1
Є посилання на це?
Дані

14
Можливо, ця дивовижна нитка відповість на всі ваші запитання? stackoverflow.com/questions/402283/stdwstring-vs-stdstring
MrFox

15
У Windows у вас насправді немає вибору. Його внутрішні API розроблені для UCS-2, що було розумно на той час, оскільки це було до того, як кодування UTF-8 та UTF-16 змінної довжини було стандартизовано. Але тепер, коли вони підтримують UTF-16, вони закінчилися гіршими з обох світів.
jamesdlin

12
utf8everywhere.org добре обговорює причини уникати широких символів.
JoeG

5
@jamesdlin Звичайно, у вас є вибір. Бібліотека nowide забезпечує зручний спосіб перетворення рядків під час переходу до API. Виклики API із рядками зазвичай є низькочастотними, тому розумним способом є перетворення ad-hok і постійно мати файли та внутрішні змінні в UTF-8.
Павло Радзивіловський

Відповіді:


114

Що таке wchar_t?

wchar_t визначається таким чином, що кодування символу будь-якої локалі може бути перетворено у представлення wchar_t, де кожен wchar_t представляє рівно одну кодову точку:

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

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

Для цього не потрібно, щоб wchar_t був достатньо великим, щоб одночасно представляти будь-який символ із усіх мов. Тобто, кодування, яке використовується для wchar_t, може відрізнятися в різних регіонах. Це означає, що ви не можете обов'язково перетворити рядок у wchar_t, використовуючи одну локаль, а потім перетворити назад у char, використовуючи іншу локаль. 1

Оскільки використання wchar_t як загального представлення між усіма локалями, здається, є основним використанням wchar_t на практиці, ви можете замислитися, для чого це добре, якщо не це.

Початковий намір і мета wchar_t полягав у спрощенні обробки тексту, визначаючи її таким чином, що вона вимагає однозначного зіставлення з кодових одиниць рядка на символи тексту, дозволяючи тим самим використовувати ті самі прості алгоритми, що використовуються за допомогою рядків ascii для роботи з іншими мовами.

На жаль, формулювання специфікації wchar_t передбачає індивідуальне відображення символів та кодових точок для досягнення цього. Юнікод порушує це припущення 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 не задовольняє вимогам __STDC_ISO_10646__.

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

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

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

Альтернатива, яка мені подобається, - це використання кодованих рядків C UTF-8, навіть на платформах, не особливо зручних для UTF-8.

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

Одна річ, яку UTF-8 не забезпечує, - це можливість використовувати прості текстові алгоритми, такі як можливі з ASCII. У цьому UTF-8 не гірше, ніж будь-яке інше кодування Unicode. Насправді це можна вважати кращим, оскільки мультикодові одиничні подання в UTF-8 є більш поширеними, тому помилки в коді, що обробляють такі подання змінної ширини символів, швидше за все будуть помічені та виправлені, ніж якщо ви намагаєтесь дотримуватися UTF -32 з NFC або NFKC.

Багато платформ використовують UTF-8 як своє власне кодування символів, і багато програм не вимагають значної обробки тексту, і тому написання інтернаціоналізованої програми на цих платформах мало чим відрізняється від написання коду без урахування інтернаціоналізації. Написання ширшого портативного коду або написання на інших платформах вимагає вставки перетворень на межі 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, достатньо, щоб сказати, що локаль `` підтримує '' цей символ, і в цьому випадку кодування змінної ширини не є законним, а використання Window UTF-16 не відповідає.

2. Юнікод дозволяє представляти багато символів із декількома кодовими точками, що створює ті самі проблеми для простих текстових алгоритмів, що і кодування змінної ширини. Навіть якщо чітко дотримуватися складеної нормалізації, для деяких символів все одно потрібні кілька кодових точок. Див .: http://www.unicode.org/standard/where/


3
Додаток: utf8everywhere.org рекомендує використовувати UTF-8 у Windows, а Boost.Nowide заплановано на офіційний огляд.
Яків Галка

2
Найкраще, звичайно, це використовувати C # або VB.Net у Windows :) Або звичайний старий C / Win32. Але якщо вам потрібно використовувати C ++, то найкращим способом буде TCHAR. За замовчуванням значення "wchar_t" на MSVS2005 і вище. ІМХО ...
paulsm4

4
@BrendanMcK: Звичайно, коду, який використовує Win32 API у вікнах та інших API в інших системах, не існує. Правда? Проблема підходу Microsoft ("використовувати wchar внутрішньо скрізь у вашому додатку") полягає в тому, що впливає навіть на код, який не взаємодіє із системою безпосередньо і може бути портативним.
Яків Галка

4
Проблема полягає в тому, що вам доводиться використовувати специфічні для Windows функції, оскільки рішення Microsoft не підтримувати UTF-8, оскільки кодова сторінка ANSI "ламає" стандартну бібліотеку C (++). Наприклад, ви не fopenможете файл, ім'я якого містить символи, що не є ANSI.
dan04

11
@ dan04 Так, ви не можете використовувати стандартну бібліотеку в Windows, але ви можете створити портативний інтерфейс, який обгортає стандартну бібліотеку на інших платформах і перетворює з UTF-8 на wchar_t безпосередньо перед використанням функцій Win32 W.
bames53

20

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

Що, на жаль, зовсім не так. "Широкі символи" не обов'язково складають 2 байти на всіх платформах, за будь-яких обставин.

Це одна з найкращих праймерів на "Unicode" (незалежно від цього питання, незалежно від C ++), яку я коли-небудь бачив: настійно рекомендую:

І я чесно вважаю, що найкращим способом боротьби з "8-бітовим ASCII" проти "Win32 wide characters" проти "wchar_t-in-general" є просто прийняття того, що "Windows відрізняється" ... і кодування відповідно.

ІМХО ...

PS:

Я повністю згоден з Джеймсдліном вище:

У Windows у вас насправді немає вибору. Його внутрішні API розроблені для UCS-2, що було розумно на той час, оскільки це було до того, як кодування UTF-8 та UTF-16 змінної довжини було стандартизовано. Але тепер, коли вони підтримують UTF-16, вони закінчилися гіршими з обох світів.

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