Запутался в std::wstring, UTF-16, UTF-8 в C++ и отображении строк в графическом интерфейсе Windows

Я работаю над C++ -программой только для английского языка для Windows, где нам сказали "всегда используйте std::wstring", но кажется, что никто в команде не имеет большого понимания, кроме этого.

Я уже прочитал вопрос под названием "std:: wstring VS std:: string. Это было очень полезно, но я все еще не совсем понимаю, как применить всю эту информацию к моей проблеме.

Программа, над которой я работаю, отображает данные в графическом интерфейсе Windows. Эти данные сохраняются в виде XML. Мы часто преобразуем этот XML с помощью XSLT в HTML или XSL:FO для целей отчетности.

Основываясь на том, что я прочитал, я чувствую, что HTML должен быть закодирован как UTF-8. Я очень мало знаю о разработке GUI, но то, что я прочитал, говорит о том, что все элементы GUI основаны на строках в кодировке UTF-16.

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

Я подозреваю, что мое объяснение могло бы использовать разъяснения, поэтому я постараюсь предоставить их, если у вас есть какие-либо вопросы.

5 ответов

Решение

Windows начиная с NT4 основана на Unicode-кодированных строках, да. Ранние версии были основаны на UCS-2, который является предшественником или UTF-16, и, таким образом, не поддерживают все символы, которые поддерживает UTF-16. Более поздние версии основаны на UTF-16. Однако не все ОС основаны на UTF-16/UCS-2. * Системы nix, например, основаны на UTF-8.

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

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

Технически std::wstring - это UCS-2: для каждого символа используются два байта, а кодовые таблицы в основном соответствуют формату Unicode. Важно понимать, что UCS-2 - это не то же самое, что UTF-16! UTF-16 допускает "суррогатные пары" для представления символов, которые находятся за пределами двухбайтового диапазона, но UCS-2 использует ровно два байта для каждого символа, точка.

Лучшее правило для вашей ситуации - выполнять перекодирование при чтении и записи на диск. Как только он окажется в памяти, сохраните его в формате UCS-2. API-интерфейсы Windows будут читать его так, как если бы он был UTF-16 (то есть, пока std::wstring не понимает концепцию суррогатных пар, если вы создаете их вручную (чего не будет, если ваш единственный язык Англ), винда их прочтет).

Всякий раз, когда вы читаете данные в форматах сериализации или вне их (например, XML) в наше время, вам, вероятно, придется выполнять транскодирование. Это неприятный и очень прискорбный факт из жизни, но он неизбежен, так как Unicode - это кодировка символов переменной ширины, и большинство символьных операций в C++ выполняются как массивы, для которых вам нужен постоянный интервал.

Инфраструктуры более высокого уровня, такие как.NET, скрывают большинство деталей, но за кадром они обрабатывают транскодирование таким же образом: изменяя данные переменной ширины на строки фиксированной ширины, манипулируя ими, а затем изменяя их обратно в кодировки переменной ширины, когда это требуется для вывода.

AFAIK, когда вы работаете с std::wstring в Windows на C++ и сохраняете с использованием UTF-8 в файлах (что звучит хорошо и разумно), вам придется преобразовывать данные в UTF-8 при записи в файл и преобразовывать обратно в UTF-16 при чтении из файла. Проверьте эту ссылку: Написание файлов UTF-8 на C++.

Я бы придерживался Visual Studio по умолчанию проекта -> Свойства -> Свойства конфигурации -> Общие -> Набор символов -> Использовать набор символов Unicode, использовать тип wchar_t (то есть с std::wstring) и не использовать тип TCHAR. (Например, я бы просто использовал wcslen-версию strlen, а не _tcslen.)

Одним из преимуществ использования std::wstring в Windows для строк, связанных с графическим интерфейсом, является то, что внутренне все вызовы Windows API используют и работают на UTF-16. Если вы когда-либо замечали, есть 2 версии всех вызовов Win32 API, которые принимают строковые аргументы. Например, "MessageBoxA" и "MessageBoxW". Оба определения существуют в, и на самом деле вы можете вызывать любое из них, но если оно включено с включенной поддержкой Юникода, произойдет следующее:

#define MessageBox MessageBoxW

Затем вы попадаете на TCHAR и другие хитрости Microsoft, чтобы попытаться упростить работу с API, которые имеют версию ANSI и Unicode. Короче говоря, вы можете вызывать любой из них, но скрыто ядро ​​Windows на основе Unicode, поэтому вы будете платить за конвертацию в Unicode для каждой строки, принимающей вызов Win32 API, если вы не используете версию с широким символом.

UTF-16 и использование ядра Windows

Даже если вы говорите, что в ваших данных только английский, вы, вероятно, ошибаетесь. Поскольку сейчас мы находимся в глобальном мире, имена / адреса / и т. Д. Имеют иностранные символы. Хорошо, я не знаю, какой тип данных у вас есть, но в целом я бы сказал, создайте ваше приложение для поддержки UNICODE как для хранения данных, так и для отображения данных пользователю. Это предполагает использование XML с UTF-8 для хранения и UNICODE-версий вызовов Windows, когда вы используете GUI. А поскольку Windows GUI использует UTF-16, где каждый токен является 16-битным, я бы предложил хранить данные в приложении в 16-битной строке. И я полагаю, что для этой цели ваш компилятор для Windows будет использовать std::wstring как 16-битный.

Итак, вам придется много конвертировать между UTF-16 и UTF-8. Сделайте это с некоторой существующей библиотекой, как например ICU.

Другие вопросы по тегам