Visual С++: Перенос традиционного строкового кода на C и С++ в мир Unicode

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

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

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

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

Я вижу так много широко распространенных символов и типов Unicode и конверсий, отсюда и моя путаница. (Пример: wchar_t, TCHAR, _T, _TEXT, TEXT и т.д.)

Ответы

Ответ 1

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

Посмотрим, есть ли у меня собственный понижающий мотив...: -/

Изменить: REJOICE!!!

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

\ о /

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

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

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

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

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

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

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

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

Чтобы обрабатывать STL с моим компилятором (в то время я работал над Visual С++ 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, но я думаю, что это нормально (и оно было протестировано нормально).

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

1.c - Сделано!!!

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

Мой совет заключается в том, что у вас, вероятно, есть режим "Отладка" и "Релиз" на вашем проекте Visual С++, чтобы создать "отладочный Unicode" и "Release Unicode", полученный из них, где описанные выше макросы определены.

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

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

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

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

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

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

Одним из распространенных заблуждений в Windows является вера в символ wchar_t - это один символ Юникода. Это неверно, так как некоторые символы 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 char широкими в Linux!

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

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

2.b - В Linux нет tchar.h

Мое решение: напишите.

Вам нужно только определить префиксные символы '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

Конечно, сопоставление 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, чтобы оставаться совместимым с кросс-платформой.

Это означает, что некоторые функции, такие как поиск присутствия ONE 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 char в Windows (не то, что я не проверял ошибку, я просто надел 't забота достаточно).

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

Не смешивайте две разные проблемы, а именно: предполагаемая ошибка в API Windows и собственный код. Ничто в вашем собственном коде не позволит избежать ошибки в API Windows, если вы НЕ используете предполагаемый прослушиваемый 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 (или другую кодированную) char в Windows. Например, предполагается, что имена файлов для библиотек stdio.h и iostream будут кодироваться, а также версия ANSI версии Win32 API (например, CreateWindowA).

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

Источник:

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

Приложение

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

Ответ 2

Я очень рекомендую против 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 за то, как это сделать проще всего (на мой взгляд).

Ответ 3

"Hello World" → L "Hello World"

char → wchar_t (если вы действительно не хотите char)

char * → wchar_t *

string → wstring

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

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

Ответ 4

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

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

Одна вещь, о которой я могу думать, может быть не очевидной во время выполнения, - это когда строка выделяется с помощью malloc без использования оператора sizeof для базового типа. Поэтому следите за такими вещами, как char * p = (char*)malloc(11) - 10 символов плюс завершающий NULL, эта строка будет в два раза меньше, чем предполагалось в wchar_t s. Он должен стать 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".

Ответ 5

Ваш вопрос включает в себя два разных, но связанных понятия. Одним из них является кодировка строки (например, 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/non-Unicode) и т.д.

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

Ответ 6

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

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