Visual C++:将传统的C和C++字符串代码迁移到Unicode世界

Ash*_*ppa 13 c c++ string unicode

我看到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类型.)

我看到很多广泛的字符和Unicode类型和转换散落在周围,因此我的困惑.(例如:wchar_t,TCHAR,_T,_TEXT,TEXT等)

Pav*_*sky 14

I recommend very much against L"", _T(), std::wstring (the latter is not multiplatform) and Microsoft recommendations on how to do Unicode.

There's a lot of confusion on this subject. Some people still think Unicode == 2 byte characters == UTF-16. Neither equality is correct.

In fact, it's possible, and even better to stay with char*and the plain std::string, plain literals and change very little (and still fully support Unicode!).

请参阅我的回答:https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful/1855375#1855375,了解如何以最简单的方式(在我看来)这样做.

  • @AAT:我不同意MS支持UTF-16.例如,当您尝试删除记事本中的4字节UTF-16字符时,文本将变为无效.我建议只在MFC/API调用附近转换为UTF-16.至少,我只为Windows编程,经过麻烦后我甚至更喜欢UTF-8. (4认同)
  • 许多微软的文档使用术语"Unicode"同义词"UTF-16"或"UCS-2" (3认同)

pae*_*bal 14

注意:哇...显然,有人决定几乎所有的答案都应该得到一个downmod,即使是正确的...我自己对它进行了修改以平衡downmod ...

让我们看看我是否有自己的downmod ......: - /

编辑:REJOICE !!!

九个小时前,有人(可能是那个对Pavel Radzivilovsky的每个答案都赞不绝口的人)低估了这个答案.当然,没有任何评论指出我的答案有什么问题.

\ O /

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 类型--->这很棘手,因为你必须创建自己的 std::tstring
  • 记住sizeof(char)可以与sizeof(TCHAR)不同,所以也要更新你的mallocs和new []

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
Run Code Online (Sandbox Code Playgroud)

通常,它没有被授权污染std命名空间,但我想这是好的(并且测试好了).

这样,您可以为大多数STL/C++ iostreams构造添加前缀,t并准备好Unicode(在Windows上).

1.c - 已经完成!!!

现在,您可以通过定义UNICODE_UNICODE定义来从ANSI模式切换到UNICODE模式,通常在项目设置中(我记得在Visual C++ 2008上,第一个设置页面中的条目完全是为了这个).

我的建议是,因为您可能在Visual C++项目上有"调试"和"释放"模式,以创建从它们派生的"调试Unicode"和"释放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 - 记住Windows上的UTF-16字形可以是1或2个wchar_t宽!

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 - 请记住,在Linux上,UTF-8字形可以是1,2,3或4个字符宽!

当然,上面关于UTF-16和宽字符的建议在这里更为重要:

Unicode字形可以char表示1到4个字符.因此,您使用的任何代码都依赖于每个char都是一个独立的Unicode字符的假设会破坏.

2.b - tchar.hLinux 上没有!

我的解决方案:写下来.

您只需要定义'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__
Run Code Online (Sandbox Code Playgroud)

...并将其包含在Linux上,而不是包含tchar.h来自Windows的.

2.c - tstringLinux 上没有!

当然,上面为Windows完成的STL映射应该完成以处理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
Run Code Online (Sandbox Code Playgroud)

现在,您可以在Linux和Windows上使用_T("Hello World")std::tstring.

3 - 必须抓住!

而且有.

首先,存在std使用您自己的t前缀符号污染命名空间的问题,这应该被禁止.然后,不要忘记宏上的添加,这将污染您的代码.在目前的情况下,我猜这是好的.

二,我认为你在Windows上使用MSVC(因此是宏_MSC_VER)和Linux上的GCC(因此是宏__GNUC__).如果您的情况不同,请修改定义.

三,您的代码必须是Unicode中性的,也就是说,您必须不依赖于您的字符串为UTF-8或UTF-16.实际上,除了ASCII字符之外,你的源代码应该是空的,以保持跨平台兼容.

这意味着某些功能,例如搜索ONE Unicode Glyph的存在,必须通过单独的代码完成,这将具有#define使其正确运行所需的所有功能.

例如,搜索字符é(Unicode Glyph 233)需要您在Windows上使用UTF-16 wchar_t时搜索第一个字符233,并在UTF-8上使用第一个两个字符195和169的序列char.这意味着您必须使用某些Unicode库来执行此操作,或者自己编写它.

但这更像是Unicode本身的问题,而不是Windows或Linux上的Unicode.

3.a - 但Windows应该不能正确处理UTF-16

所以呢?

我看到的"规范"示例是EDIT Win32控件,它应该无法在Windows上正确退格非BMP UTF-16字符(不是我没有验证错误,我只是不在乎) .

这是Microsoft的问题.您在代码中决定的任何内容都不会改变Win32 API中存在或不存在此错误的事实.因此,在Windows上使用UTF-8字符不会纠正EDIT控件上的错误.您唯一可以做的就是创建自己的EDIT控件(将其子类化并正确处理BACKSPACE事件?)或您自己的转换函数.

不要混淆两个不同的问题,即:Windows API中的假定错误您自己的代码.除非您不使用假设的错误Windows API,否则您自己的代码中的任何内容都不会避免Windows API中的错误.

3.b - 但Windows上的UTF-16,Linux上的UTF-8并不复杂吗?

是的,它可能会导致某些平台上的错误,如果你对字符的假设过多,那么其他平台上就不会发生错误.

我假设你的主要平台是Windows(或者你想为两者wchar_tchar用户提供一个库).

但如果不是这种情况,如果Windows不是你的主要平台,那么就有一个假设所有char和std :: string都包含UTF-8字符的解决方案,除非说明不同.然后,您需要包装API以确保您的char UTF-8字符串不会被误认为是Windows上的ANSI(或其他代码管理的)字符串.例如,假定stdio.hiostream库的文件名称是代码分段的,以及Win32 API的ANSI版本(例如,CreateWindowA).

这是使用UTF-8字符的GTK +的方法,但令人惊讶的是,使用UTF-16的QT(构建Linux KDE).

资源:

不过,它不会保护你免受"嘿,但Win32编辑控件不能处理我的unicode字符!" 问题,所以你仍然必须将该控件子类化以获得所需的行为(如果bug仍然存在)......

附录

请参阅我的回答 std :: wstring VS std :: string for std::stringstd::wstring.之间的完全区别.


Vit*_*lov -4

  • 用 _T() 围绕你的文字常量,例如 _T("Hello world")
  • 替换char为宏CHAR
  • 将字符串替换为wstring

那么一切都应该有效。