Как мне заставить STL std :: string работать с юникодом в windows? - PullRequest
9 голосов
/ 15 июля 2010

В моей компании у нас есть кроссплатформенная (Linux & Windows) библиотека, которая содержит наше собственное расширение STL std :: string, этот класс обеспечивает все виды функций поверх строки; split, format, to / from base64 и т. д. Недавно нам было предложено сделать эту строку unicode «дружественной», в основном она должна поддерживать символы из китайского, японского, арабского и т. д. После первоначального исследования это выглядит нормально на стороне Linux поскольку каждая вещь по своей сути является UTF-8, однако у меня проблемы со стороной Windows; Есть ли хитрость, чтобы заставить STL std :: string работать как UTF-8 на Windows? Это вообще возможно? Есть ли способ лучше? В идеале мы должны основываться на std :: string, поскольку именно на этом основан класс string в Linux.

Спасибо,

Ответы [ 8 ]

12 голосов
/ 15 июля 2010

В вашем вопросе есть несколько неправильных представлений.

  • Ни C ++, ни STL не имеют дело с кодировками.

  • std::string по сутистрока байтов , а не символов .Таким образом, у вас не должно возникнуть проблем с вставкой в ​​него кодированного в UTF-8 Unicode.Однако имейте в виду, что все функции string также работают с байтами, поэтому myString.length() даст вам число байтов, а не количество символов.

  • Linux - не по своей сути UTF-8.В настоящее время большинство дистрибутивов по умолчанию используют UTF-8, но на него не следует полагаться.

8 голосов
/ 19 июля 2010

Да - благодаря большей осведомленности о локалях и кодировках.

В Windows есть два вызова функций для всего, что требует текста: FoobarA () и FoobarW ().Функции * W () принимают строки в кодировке UTF-16, * A () принимает строки в текущей кодовой странице.Однако Windows не поддерживает кодовую страницу UTF-8, поэтому вы не можете напрямую использовать ее в этом смысле с функциями * A (), и при этом вы не хотите зависеть от того, что установлено пользователями.Если вы хотите использовать Unicode в Windows, используйте функции с поддержкой Unicode (* W).Существуют учебные пособия, которые вы можете найти в Googling «Учебник по Unicode Windows».

Если вы храните данные UTF-8 в std :: string, то перед тем, как передать их в Windows, преобразуйте их вUTF-16 (Windows предоставляет функции для этого), а затем передает его в Windows.

Многие из этих проблем возникают из-за того, что C / C ++ обычно не зависит от кодировки.char на самом деле не персонаж, это просто целостный тип.Даже использование массивов char для хранения данных UTF-8 может создать проблемы, если вам потребуется доступ к отдельным кодовым единицам, поскольку подпись char не определена стандартами.Оператор типа str[x] < 0x80 для проверки многобайтовых символов может быстро привести к ошибке.(Это утверждение всегда верно, если char подписано.) Единица кода UTF-8 - это целочисленный тип без знака с диапазоном 0-255.Это точно соответствует типу C uint8_t, хотя unsigned char также работает.В идеале тогда я бы сделал строку UTF-8 массивом uint8_t с, но из-за старых API это делается редко.

Некоторые люди рекомендовали wchar_t, утверждая, что это "Тип символов Unicode "или что-то в этом роде.Опять же, здесь стандарт такой же независимый, как и раньше, так как C предназначен для работы где угодно и где угодно, где Unicode не используется.Таким образом, wchar_t не более Unicode, чем char.Стандарт гласит:

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

InLinux, wchat_t представляет кодовую единицу UTF-32 / кодовую точку.Таким образом, это 4 байта.Однако в Windows это кодовая единица UTF-16 и занимает всего 2 байта.(Что, я бы сказал, не соответствует вышесказанному, поскольку 2 байта не могут представлять весь Unicode, но именно так он и работает.) Разница в размерах и разница в кодировании данных явно создает нагрузку на переносимость.Сам стандарт Unicode рекомендует против wchar_t, если вам нужна мобильность.(§5.2)

Конечный урок: Мне проще всего хранить все мои данные в каком-то хорошо объявленном формате.(Обычно это UTF-8, обычно в std :: string, но мне бы хотелось что-нибудь получше.) Здесь важна не часть UTF-8, а, скорее, я знаю , что мои строкиUTF-8.Если я передаю их другому API, я также должен знать , что этот API ожидает строки UTF-8.Если это не так, то я должен преобразовать их.(Таким образом, если я говорю с API-интерфейсом Window, я должен сначала преобразовать строки в UTF-16.) Текстовая строка UTF-8 представляет собой «оранжевый», а текстовая строка «latin1» - «яблоко».Массив char, который не знает, в какой кодировке он находится, - это путь к катастрофе.

7 голосов
/ 15 июля 2010

Поместить кодовые точки UTF-8 в std::string должно быть хорошо независимо от платформы. Проблема в Windows заключается в том, что с UTF-8 больше ничего не ожидает и не работает - вместо этого он работает и работает с UTF-16. Вы можете переключиться на std::wstring, в котором будет храниться UTF-16 (по крайней мере, на большинстве компиляторов Windows), или вы можете написать другие подпрограммы, которые будут принимать UTF-8 (возможно, путем преобразования в UTF-16 и последующей передачи в ОС. ).

4 голосов
/ 15 июля 2010

Вы смотрели на std::wstring?Это версия std::basic_string для wchar_t, а не char, которую использует std::string.

2 голосов
/ 16 июля 2010

Если вы хотите избежать головной боли, вообще не используйте строковые типы STL.C ++ ничего не знает о Unicode или кодировках, поэтому для переносимости лучше использовать библиотеку, специально предназначенную для поддержки Unicode, например библиотеку ICU.ICU по умолчанию использует строки UTF-16, поэтому преобразование не требуется и поддерживает преобразования во многие другие важные кодировки, такие как UTF-8.Также попробуйте использовать кросс-платформенные библиотеки, такие как Boost.Filesystem для таких вещей, как манипуляции с путями (boost::wpath).Избегайте std::string и std::fstream.

2 голосов
/ 15 июля 2010

Нет, нет способа заставить Windows обрабатывать "узкие" строки как UTF-8.

Вот что мне лучше всего подходит в этой ситуации (кроссплатформенное приложение, которое имеет сборки для Windows и Linux).

  • Используйте std :: string в кроссплатформенной части кода.Предположим, что он всегда содержит строки UTF-8.
  • В части кода Windows явно используйте «широкие» версии Windows API, т.е. напишите, например, CreateFileW вместо CreateFile.Это позволяет избежать зависимости от конфигурации системы сборки.
  • На уровне абстракции platfrom конвертировать между UTF-8 и UTF-16, где это необходимо (MultiByteToWideChar / WideCharToMultiByte).

Другие подходычто я пробовал, но мне не очень нравится:

  • typedef std::basic_string<TCHAR> tstring;, затем используйте tstring в бизнес-коде.Обертки / перегрузки могут быть сделаны для упрощения преобразования между std :: string и std :: tstring, но это все равно добавляет много боли.
  • Используйте std::wstring везде.Не очень помогает, поскольку wchar_t для Windows является 16-битным, поэтому вам нужно либо ограничить себя BMP, либо пойти на множество сложностей, чтобы код работал с Unicode кроссплатформенным.В последнем случае все преимущества по сравнению с UTF-8 испаряются.
  • Используйте ATL / WTL / MFC CString в определенной для платформы части;используйте std::string в кросс-платформенной части.На самом деле это вариант того, что я рекомендую выше.CString во многих отношениях превосходит std::string (по моему мнению).Но это вводит дополнительную зависимость и поэтому не всегда приемлемо или удобно.
1 голос
/ 18 июля 2010

В библиотеке времени выполнения Windows API и C параметры char* интерпретируются как закодированные в кодовой странице "ANSI".Проблема в том, что UTF-8 не поддерживается как кодовая страница ANSI , что мне кажется невероятно раздражающим .

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

  • Использование UTF-8 в качестве кодировки по умолчанию для строк.
  • В специфичном для Windows коде всегда вызывайте версию "W"функции, преобразующие строковые аргументы между UTF-8 и UTF-16 по мере необходимости.

Это также подход, который Poco принял .

0 голосов
/ 27 ноября 2016

Это действительно зависит от платформы, Unicode - это головная боль. Зависит от того, какой компилятор вы используете. Для более старых из MS (VS2010 или старше) вам потребуется использовать API, описанный в MSDN

для VS2015

std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt"s;

в соответствии с их документами. Я не могу это проверить.

для MINGW, GCC и т. Д.

std::string _old = u8"D:\\Folder\\This \xe2\x80\x93 by ABC.txt";
std::cout << _old.data();

вывод содержит правильное имя файла ...

...