我看到Visual Studio 2008和更高版本现在开始了一个新的解决方案,将字符集设置为Unicode。我的旧C++代码只处理英文ASCII文本,并且充满了:
- 像
"Hello World"
这样的文字字符串 char
型char *
指向已分配的C字符串的指针STL string
型
1.使用STL string
构造函数(接受const char *
)和STL string.c_str()
从STL string
转换为C字符串,反之亦然
1.我需要做哪些更改才能迁移此代码,使其在Visual Studio Unicode和启用Unicode的库的生态系统中工作?(我没有真实的需要它同时使用ASCII和Unicode,它可以是纯Unicode。)
1.是否也可以以独立于平台的方式实现这一点?(即不使用Microsoft类型。)
我看到这么多宽字符和Unicode类型和转换分散在周围,因此我的困惑。(例如:wchar_t、TCHAR、_T、_TEXT、TEXT等)
6条答案
按热度按时间8i9zcol21#
Note: Wow... Apparently, SOMEONE decided that ALMOST all answers deserved a downmod, even when correct... I took upon myself of upmoding them to balance the downmod...
``
Let's see if I have my own downmod... :-/
编辑:REJOICE!!!
九个小时前,
someone
(可能是那个否决了除了帕维尔·拉齐维洛夫斯基之外的所有答案的人)否决了这个答案。当然,没有任何评论指出我的答案有什么问题。\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
type --->这很棘手,因为您必须创建自己的std::tstring
1.b -您自己的
tstring.hpp
头为了用我的编译器处理STL(当时,我正在使用Visual C++ 2003,所以你的里程可能会有所不同),我必须提供一个
tstring.hpp
头,它是跨平台的,并允许用户使用tstring,tiostream等。我不能把完整的源代码放在这里,但我会给予一个摘录,使您能够产生自己的:通常情况下,它没有被授权污染
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 -记住UTF-16字形在Windows上的宽度可以是1或2 wchar_t!
Windows上一个常见的误解是认为wchar_t字符是一个Unicode字符。这是错误的,因为一些Unicode字形由两个wchar_t表示。
因此,如果您使用的不是来自BMP的Unicode字形,则任何依赖于一个
char
作为一个字符串的代码都可能会中断。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编码可能需要1到4个
char
字符来表示。因此,任何依赖于假设每个char
都是独立Unicode字符的代码都将中断。2.b -Linux上没有
tchar.h
!我的解决方案:写的
您只需要定义前缀为“t”的符号来Map普通符号,如以下摘录所示:
.并将其包含在Linux上,而不是包含Windows中的
tchar.h
。2.c -Linux上没有
tstring
!当然,上面为Windows完成的STLMap应该完成以处理Linux的情况:
现在,您可以在Linux和Windows上使用
_T("Hello World")
和std::tstring
。3 -一定有陷阱!
确实有
首先,
std
命名空间会被你自己的t
前缀符号污染,这是应该被禁止的。然后,不要忘记在宏上添加,这会污染你的代码。在目前的情况下,我想这是好的。第二,我假设你在Windows上使用MSVC(因此是宏
_MSC_VER
),在Linux上使用GCC(因此是宏__GNUC__
)。如果您的情况不同,请修改定义。第三,你的代码必须是Unicode中立的,也就是说,你不能依赖你的字符串是UTF-8或UTF-16。事实上,为了保持跨平台兼容性,您的源代码应该除了ASCII字符之外什么都没有。
这意味着一些功能,比如搜索一个Unicode Glance的存在,必须由一段单独的代码来完成,这段代码将包含所有正确的
#define
。例如,在Windows上使用UTF-16 wchar_t时,搜索字符
é
(Unicode Glance 233)需要搜索第一个字符233,而在UTF-8char
上搜索第一个由两个字符195和169组成的序列。这意味着你必须使用一些Unicode库来完成它,或者自己编写它。但这更多的是Unicode本身的问题,而不是Windows或Linux上的Unicode。
3.a -但是Windows应该不能正确处理UTF-16
那又怎样?
我看到的“规范”例子是EDIT Win32控件,它应该无法在Windows上正确地退格非BMP UTF-16字符(并不是说我没有验证这个错误,我只是不够关心)。
这是微软的问题。您在代码中决定的任何内容都不会改变Win32 API中是否存在此错误的事实。因此在Windows上使用UTF-8字符不会纠正EDIT控件上的错误。您唯一希望做的事情就是创建自己的EDIT控件(将其子类化并正确处理EDITSPACE事件?)或您自己的转换函数。
不要混淆两个不同的问题,即:
a supposed bug in the Windows API
和your own code
。在你自己的代码中没有任何东西可以避免Windows API中的错误,除非你不使用假定的有错误的Windows API。3.b -但是Windows上的UTF-16,Linux上的UTF-8,不是很复杂吗?
是的,如果你对角色假设太多,它可能会导致一些平台上的bug,而这些bug在其他平台上不会发生。
我假设您的主要平台是Windows(或者您希望为
wchar_t
和char
用户提供一个库)。但如果不是这样,如果Windows不是你的主要平台,那么有一个解决方案,假设所有的char和std::string都包含UTF-8字符,除非另有说明。然后,您需要 Package API,以确保您的字符UTF-8字符串不会被误认为是Windows上的ANSI(或其他代码封装)字符串。例如,
stdio.h
和iostream
库的文件名将被假定为已编码,以及Win32 API的ANSI版本(例如,Windows WindowA)。这是使用UTF-8字符的GTK+的方法,但并不奇怪,QT(Linux KDE构建于此)使用UTF-16。
资料来源:
尽管如此,它不会保护你从“嘿,但Win32编辑控件不处理我的Unicode字符!“问题,所以你仍然需要子类化该控件以获得所需的行为(如果bug仍然存在)。
附录
关于
std::string
和std::wstring
之间的完全区别,请参阅我在std::wstring VS std::string上的回答。yyhrrdl82#
我非常推荐反对
L""
,_T()
,std::wstring
(后者不是多平台的)和微软关于如何做Unicode的建议。在这个问题上有很多困惑。有些人仍然认为Unicode == 2字节字符== UTF-16。这两种平等都是不正确的。
事实上,这是可能的,甚至更好地保持char* 和普通的
std::string
,普通的文字和变化很少(仍然完全支持Unicode!).请看我在“UTF-16应该被认为是有害的吗?”“:https://softwareengineering.stackexchange.com/a/102215如何做它最简单的(在我看来)方法。
g9icjywg3#
我建议不要担心同时支持asphalt和unicode build(a-la TCHAR),直接使用unicode。这样你就可以使用更多的平台无关的函数(wcscpy,wcsstr等),而不是依赖于
TCHAR
函数,这些函数是Micrpsoft特定的。您可以使用std::wstring而不是std::string,并将所有
char
替换为wchar_t
。有了这样一个巨大的变化,我发现你从一件事开始,让编译器引导你到下一件事。我能想到的在运行时可能不明显的一件事是,在没有对底层类型使用
sizeof
操作符的情况下,用malloc分配字符串。所以要注意像char * p = (char*)malloc(11)
- 10个字符加上终止NULL这样的东西,这个字符串将是wchar_t
s中应有大小的一半。应该是wchar_t * p = (wchar_t*)malloc(11*sizeof(wchar_t))
。哦,整个
TCHAR
是支持编译时ASCII/Unicode字符串。它的定义是这样的:因此,在unicode配置中,
_T("blah")
变为L"blah"
,而在asc配置中,它是"blah"
。w8ntj3qf4#
“Hello World”-> L“Hello World”
char -> wchar_t(除非你真的需要char)
char * -> wchar_t *
string -> wstring
这些都是独立于平台的。但是,请注意,宽字符在不同的平台上可能不同(在Windows上为两个字节,在其他平台上为四个字节)。
在项目中定义UNICODE和_UNICODE(在Visual Studio中,您可以通过在设置中将项目设置为使用Unicode来完成此操作)。这也使得_T、TCHAR、_TEXT和TEXT宏自动变为L。这些都是微软特有的,所以如果你想跨平台的话就避免这些。
cig3rfwq5#
你的问题涉及两个不同但相关的概念。其中之一是字符串的编码(例如Unicode/ASCII)。另一个是用于字符表示的数据类型。
从技术上讲,你可以有一个使用普通
char
和std::string的Unicode应用程序。您可以使用十六进制(“\x5FA”)或八进制(“\05FA”)格式的文字来指定字符串的字节序列。注意,使用这种方法,已经存在的包含ASCII字符的字符串文字应该仍然有效,因为Unicode保留了ASCII的代码。需要注意的一点是,许多与字符串相关的函数需要小心使用。这是因为它们将对字节而不是字符进行操作。例如,
std::string::operator[]
可能会给予一个特定的字节,它只是Unicode字符的一部分。在Visual Studio中,
wchar_t
被选为基础字符类型。因此,如果你正在使用基于微软的库,如果你遵循了其他人在这里发布的许多建议,事情应该会变得更容易。用wchar_t
替换char
,使用“T”宏(如果你想保持Unicode/非Unicode之间的透明性),等等。然而,我不认为跨库使用Unicode有一个事实上的标准,因为它们可能有不同的策略来处理它。
oaxa6hgo6#
char
替换为宏CHAR
所有的工作都应该工作。