Visual C ++: перенос традиционного строкового кода на C и C ++ в мир Unicode - PullRequest
13 голосов
/ 13 января 2010

Я вижу, что Visual Studio 2008 и более поздние версии теперь запускают новое решение с набором символов Unicode. Мой старый код C ++ имеет дело только с английским текстом ASCII и полон:

  • Буквальные строки типа "Hello World"
  • char тип
  • char * указатели на выделенные строки C
  • STL string тип
  • Преобразование из STL string в строку C и наоборот с использованием конструктора STL string (который принимает const char *) и STL string.c_str()

    1. Какие изменения необходимо внести для переноса этого кода, чтобы он работал в экосистеме библиотек Visual Studio Unicode и Unicode? (У меня нет реальной необходимости работать с ASCII и Unicode, это может быть чистый Unicode.)

    2. Возможно ли сделать это независимо от платформы? (т.е. без использования типов Microsoft.)

Я вижу так много разбросанных по широким символам и Юникоду типов и преобразований, отсюда мое замешательство. (Например: wchar_t, TCHAR, _T, _TEXT, TEXT и т. Д.)

Ответы [ 6 ]

14 голосов
/ 07 сентября 2010

Примечание: Ух ты ... Похоже, КТО-то решил, что ПОЧТИ все ответы заслуживают снижения, даже когда они правильные ... Я взял на себя обязательство изменить их, чтобы сбалансировать снижение ...

Давайте посмотрим, есть ли у меня собственный downmod ...: - /

Редактировать: РАДОСТЬ !!!

Девять часов назад, кто-то (вероятно, тот, кто отрицал все ответы, кроме ответа Павла Радзивиловского) отклонил этот ответ. Конечно, без каких-либо комментариев, указывающих на то, что не так с моим ответом.

\ о /

1 - Как выполнить миграцию в Windows Unicode?

Какие изменения необходимо внести для переноса этого кода, чтобы он работал в экосистеме библиотек Visual Studio Unicode и Unicode? (У меня нет реальной необходимости работать с ASCII и Unicode, это может быть чистый Unicode.)

1.a - Моя кодовая база большая, я не могу сделать это за один шаг!

Давайте представим, что вы хотите сделать это постепенно (потому что ваше приложение не маленькое).

У меня была такая же проблема в моей команде: я хотел создать код, готовый к Unicode, сосуществующий с кодом, который не был готов к Unicode.

Для этого вы должны использовать заголовок MS tchar.h и использовать его возможности. Используя ваши собственные примеры:

  • "Hello World" ----> _T("Hello World")
  • char тип ----> TCHAR тип
  • char * указатели на выделенные строки C ----> TCHAR * указатели
  • std::string type ---> Это сложно, потому что вы должны создать свой собственный std::tstring
  • помните, что sizeof (char) может отличаться от sizeof (TCHAR), поэтому обновите ваши malloc и новые [] тоже

1.b - Ваш собственный tstring.hpp заголовок

Для обработки STL с моим компилятором (в то время я работал над Visual C ++ 2003, поэтому ваш пробег мог варьироваться), я должен предоставить заголовок tstring.hpp, который является кроссплатформенным и позволяет пользователю используйте tstring, tiostream и т. д. Я не могу поместить полный исходный код здесь, но я приведу выдержку, которая позволит вам создавать свои собственные:

namespace std
{

#ifdef _MSC_VER

#ifdef UNICODE
typedef             wstring                         tstring ;
typedef             wistream                        tistream ;
// etc.
#else // Not UNICODE
typedef             string                          tstring ;
typedef             istream                         tistream ;
// etc.
#endif

#endif

} // namespace std

Обычно, это не разрешено загрязнять пространство имен std, но я думаю, что это нормально (и это было проверено хорошо).

Таким образом, вы можете использовать префикс большинства конструкций STL / C ++ iostreams с t и иметь его готовым к Unicode (в Windows).

1.c - Готово !!!

Теперь вы можете переключиться из режима ANSI в режим UNICODE, определив UNICODE и _UNICODE, которые обычно определяются в настройках проекта (я помню, что в Visual C ++ 2008 на первых страницах настроек есть записи именно для этого ).

Мой совет таков: у вас, вероятно, есть режимы «Debug» и «Release» в вашем проекте Visual C ++, чтобы создавать производные от них режимы «Debug Unicode» и «Release Unicode», где описаны макросы, описанные выше. определены.

Таким образом, вы сможете создавать двоичные файлы ANSI и UNICODE.

1.d - Теперь все является (или должно быть) Unicode!

Если вы хотите, чтобы ваше приложение было кроссплатформенным, игнорируйте этот раздел.

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

  • _T("Hello World") ----> L"Hello World"
  • TCHAR тип ----> wchar_t тип
  • TCHAR * указатели на выделенные строки C ----> wchar_t * указатели
  • std::tstring тип ---> std::wstring тип и т. Д.

1.e - Помните, что глифы UTF-16 могут иметь ширину 1 или 2 wchar_t в Windows!

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

Таким образом, любой код, который полагается на один char, являющийся одним глифом, потенциально сломается, если вы используете глифы Unicode не из BMP.

2 - Кросс-платформенность?

Возможно ли сделать это независимо от платформы? (т.е. не используя типы Microsoft.)

Так вот, это была сложная часть.

Linux (я не знаю, для других ОС, но это должно быть легко сделать вывод из решения Linux или Windows), теперь готов к Unicode, тип char должен содержать значение UTF-8.

Это означает, что ваше приложение, когда-то скомпилированное, например, на моем Ubuntu 10.04, по умолчанию является Unicode.

2.a - Помните, что глифы UTF-8 могут иметь ширину 1, 2, 3 или 4 символа в Linux!

Конечно, совет выше относительно UTF-16 и широких символов здесь еще более важен:

Символу Юникода может потребоваться от 1 до 4 char символов для представления. Таким образом, любой используемый вами код, основанный на предположении, что каждый char является целым символом Unicode, сломается.

2.b - В Linux нет tchar.h! 1127 * Мое решение: напиши. Вам нужно только определить префиксные символы 't' для отображения на нормальные символы, как показано в этом фрагменте: #ifdef __GNUC__ #ifdef __cplusplus extern "C" { #endif #define _TEOF EOF #define __T(x) x // etc. #define _tmain main // etc. #define _tprintf printf #define _ftprintf fprintf // etc. #define _T(x) __T(x) #define _TEXT(x) __T(x) #ifdef __cplusplus } #endif #endif // __GNUC__ ... и включить его в Linux вместо tchar.h из Windows. 2.c - в Linux нет tstring! 1138 * Конечно, приведенное выше сопоставление STL для Windows должно быть завершено для обработки случая Linux: namespace std { #ifdef _MSC_VER #ifdef UNICODE typedef wstring tstring ; typedef wistream tistream ; // etc. #else // Not UNICODE typedef string tstring ; typedef istream tistream ; // etc. #endif #elif defined(__GNUC__) typedef string tstring ; typedef istream tistream ; // etc. #endif } // namespace std Теперь вы можете использовать _T("Hello World") и std::tstring в Linux и Windows. 3 - Должен быть подвох! И есть. Во-первых, существует проблема загрязнения пространства имен std вашими собственными префиксными символами t, что должно быть запрещено. Затем не забудьте добавить макросы, которые будут загрязнять ваш код. В текущем случае, я думаю, это нормально. Во-вторых, я предположил, что вы используете MSVC в Windows (таким образом, макрос _MSC_VER) и GCC в Linux (таким образом, макрос __GNUC__). Измените определения, если ваш случай отличается. В-третьих, ваш код должен быть нейтральным Unicode, то есть вы не должны полагаться на то, что ваши строки являются UTF-8 или UTF-16. Фактически, ваш источник должен быть пустым от чего угодно, кроме символов ASCII, чтобы оставаться кросс-платформенным. Это означает, что некоторые функции, такие как поиск ОДНОГО Unicode Glyph, должны выполняться отдельным фрагментом кода, который будет иметь все #define, необходимое для его исправления. Например, поиск символа é (Unicode Glyph 233) потребует от вас поиска первого символа 233 при использовании UTF-16 wchar_t в Windows и первой последовательности из двух символов 195 и 169 в UTF-8 char. Это означает, что вы должны либо использовать некоторую библиотеку Unicode, либо написать ее самостоятельно. Но это больше проблема самого Unicode, чем Unicode в Windows или в Linux. 3.a - Но предполагается, что Windows неправильно обрабатывает UTF-16

И что?

«Каноническим» примером, который я видел, описан был элемент управления EDIT Win32, который, как предполагается, не может корректно возвращать символ не-BMP UTF-16 в Windows. достаточно осторожно).

Это проблема Microsoft. Ничто из того, что вы решите в своем коде, не изменит факт наличия этой ошибки или ее отсутствия в Win32 API Поэтому использование символов UTF-8 в Windows не исправит ошибку в элементе управления EDIT. Единственное, на что вы можете надеяться - это создать свой собственный элемент управления EDIT (сделать его подклассом и правильно обрабатывать событие BACKSPACE?) Или свои собственные функции преобразования.

Не смешивайте две разные проблемы, а именно: предполагаемая ошибка в Windows API и ваш собственный код . Ничто в вашем собственном коде не поможет избежать ошибки в Windows API, если вы НЕ используете предполагаемый ошибочный Windows API.

3.b - Но UTF-16 в Windows, UTF-8 в Linux, не так ли сложно?

Да, это может привести к ошибкам на некоторых платформах, которые не произойдут на других, если вы слишком много думаете о символах.

Я предположил, что вашей основной платформой была Windows (или вы хотели предоставить библиотеку для wchar_t и char пользователей).

Но если это не так, если Windows не является вашей основной платформой, то есть решение, что все ваши символы char и std :: string будут содержать символы UTF-8, если не указано иное. Затем вам нужно будет обернуть API, чтобы убедиться, что ваша строка char UTF-8 не будет ошибочно принята за строковую строку ANSI (или другую кодовую страницу) в Windows. Например, имя файлов для библиотек stdio.h и iostream будет считаться кодированной страницей, а также версия ANSI API Win32 (например, CreateWindowA).

Это подход GTK +, который использует символы UTF-8, но не удивительно, что QT (на котором построен Linux KDE), который использует UTF-16.

Источник:

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

Приложение

См. Мой ответ в std :: wstring VS std :: string для полной разницы между std::string и std::wstring.

14 голосов
/ 13 января 2010

Я очень рекомендую против L"", _T(), std::wstring (последний не мультиплатформенный) и рекомендаций Microsoft о том, как сделать Unicode.

В этом вопросе много путаницы. Некоторые люди все еще думают, что Unicode == 2-байтовые символы == UTF-16. Ни одно равенство не является правильным.

На самом деле, возможно , и даже лучше остаться с char * и простым std::string, обычными литералами и очень мало изменяться (и при этом полностью поддерживать Unicode!).

Смотрите мой ответ здесь: https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful/1855375#1855375 о том, как сделать это самым простым (на мой взгляд) способом.

2 голосов
/ 13 января 2010

Я бы посоветовал не беспокоиться о поддержке сборки ascii и unicode (а-ля TCHAR) и сразу перейти к unicode. Таким образом, вы получаете возможность использовать больше независимых от платформы функций (wcscpy, wcsstr и т. Д.) Вместо того, чтобы полагаться на TCHAR функции, специфичные для Micrpsoft.

Вы можете использовать std :: wstring вместо std :: string и заменить все char s на wchar_t s. С таким огромным изменением я обнаружил, что вы начинаете с одного, а компилятор подсказывает вам следующее.

Одна вещь, о которой я могу подумать, что может быть неочевидной во время выполнения, это когда строка выделяется с помощью malloc без использования оператора sizeof для базового типа. Так что следите за вещами вроде char * p = (char*)malloc(11) - 10 символов плюс завершающий NULL, эта строка будет вдвое меньше, чем она должна быть в wchar_t с. Это должно стать wchar_t * p = (wchar_t*)malloc(11*sizeof(wchar_t)).

Да, и в целом TCHAR должен поддерживать строки ASCII / Unicode времени компиляции. Это определено примерно так:

#ifdef _UNICODE
#define _T(x) L ## x
#else
#define _T(x) ## x
#endif

Так что в конфигурации Unicode _T("blah") становится L"blah", а в конфигурации ASCII это "blah".

2 голосов
/ 13 января 2010

"Hello World" -> L "Hello World"

char -> wchar_t (если вы на самом деле не хотите char)

char * -> wchar_t *

строка -> wstring

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

Определите UNICODE и _UNICODE в вашем проекте (в Visual Studio вы можете сделать это, установив в настройках проекта использование Unicode). Это также делает макросы _T, TCHAR, _TEXT и TEXT автоматически становятся L. Это специфично для Microsoft, поэтому избегайте их, если вы хотите быть кроссплатформенным.

1 голос
/ 13 января 2010

Ваш вопрос включает в себя два разных, но взаимосвязанных понятия. Одним из них является кодировка строки (например, Unicode / ASCII). Другой тип данных используется для представления символов.

Технически, вы можете иметь приложение Unicode, используя обычные char и std :: string. Вы можете использовать литералы в шестнадцатеричном ("\ x5FA") или восьмеричном ("\ 05FA") формате, чтобы указать последовательность байтов строки. Обратите внимание, что при таком подходе ваши уже существующие строковые литералы, содержащие символы ASCII, должны оставаться действительными, поскольку Unicode сохраняет коды из ASCII.

Важно отметить, что многие функции, связанные со строками, должны использоваться осторожно. Это потому, что они будут работать с байтами , а не с символами . Например, std::string::operator[] может дать вам определенный байт, который является только частью символа Unicode.

В Visual Studio wchar_t был выбран в качестве основного типа символов. Поэтому, если вы работаете с библиотеками на базе Microsoft, вам будет легче, если вы будете следовать многим советам, размещенным здесь другими. Замена char на wchar_t с использованием макросов «T» (если вы хотите сохранить прозрачность между Unicode / не-Unicode) и т. Д.

Однако я не думаю, что существует де-факто стандарт работы с Unicode в разных библиотеках, поскольку они могут иметь разные стратегии для его обработки.

0 голосов
/ 13 января 2010
  • Вокруг ваших буквальных констант с помощью _T (), например _T ("Привет, мир")
  • Заменить char на макросы CHAR
  • Заменить строку на wstring

Тогда все должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...