为什么我们从MultiByte转换到WideChar?
我用来对付ASCII字符串但现在UNICODE我太多困惑的一些术语:为什么我们从MultiByte转换到WideChar?
什么是多字节字符,什么是widechar
有什么区别? 多字节是指在内存中包含多个字节的字符,而widechar
只是一种表示它的数据类型?
- 我们为什么要从
MultiByteToWideChar
和WideCharToMultiByte
转换?
如果我声明是这样的:
wchar_t* wcMsg = L"مرحبا";
MessageBoxW(0, wcMsg, 0, 0);
它正确地打印消息,如果我定义UNICODE
但为什么我没有从这里WideCharToMultiByte
转换?
什么角色之间的差异设定在我的项目:
_MBCS
和UNICODE
?MSDN将“Windows API”混淆的最后一件事是UTF-16。
任何人都可以解释一些例子。一个很好的说明真的很感激。
ASCII字符串的char宽度为一个字节(通常为8位,很少为7,9或其他位宽度)。这是一个遗留下来的时间,当内存大小非常小且昂贵时,处理器通常每个指令只能处理一个字节。
由于很容易想象,一个字节远远不足以存储世界上所有可用的字形。仅中国人就有87.000个字形。字符通常只能处理256个字形(8位字节)。 ASCII只定义了96个字形(加上低32个字符,它们被定义为不可打印的控制字符)。这对于英文上下文字符,数字和一些交织符号以及其他字形来说已经足够了。
要处理比一个字节可容纳的更多字形,一种方法是将基本字形存储在一个字节中,其他常见字形存储在两个字节中,并且很少使用3字节或甚至更多字节中的字形。这种方法被称为Multi byte char set or Variable-width encoding。一个非常常见的例子是UTF 8,其中一个字符使用1到4个字节。它将ASCII字符集存储在一个字节中(因此它也向后兼容ASCII)。最高位被定义为一个开关:如果它被设置,其他字节将会跟随。这同样适用于以下字节,以便形成最多4个字节的“链”。 可变宽度的字符集的亲的是:
- 与8位ASCII字符集向后兼容性
- 存储器友好 - 使用作为较少的内存尽可能
的缺点是:
- 处理更困难,处理器更昂贵。您不能简单地迭代一个字符串,并假定每个
myString[n]
都提供一个字形;相反,如果有更多的字节在后面,你必须评估每个字节。
另一种方法是将每个字符存储在由n个字节组成的固定长度字中,该字长度足以容纳所有可能的字形。这被称为固定宽度字符集;所有的字符都有相同的宽度。一个众所周知的例子是UTF32。它是32位宽,可以将所有可能的字符存储在一个字中。固定宽度字符集的pro和con显然是可变宽度字符集的反面:内存较重但更容易迭代。
但是,即使在UTF32可用之前,Microsoft也选择了它们的本地字符集:它们使用UTF16作为Windows的字符集,它使用的字长至少为2个字节(16位)。这足以存储比单字节字符集更多的字形,但不是全部。考虑到这一点,微软在“多字节”和“Unicode”之间的区别在今天有点误导,因为它们的unicode实现也是一个多字节字符集 - 只有一个字形的最小尺寸更大。有人说这是一个很好的妥协,有人说这是两个世界中最糟糕的 - 无论如何,就是这样。当时(Windows NT)它是唯一可用的Unicode字符集,从这个角度来看,它们在多字符和Unicode之间的区别在当时是正确的(参见Raymond Chen的评论)
当然,如果你想将一个字符串以一种编码(比如UTF8)转换为另一种(比如说UTF16),您必须转换它们。那就是MultiByteToWideChar
为你做的,WideCharToMultiByte
反过来。还有一些其他的转换函数和库。
这种转换的成本相当多的时间,所以得出的结论是:如果你大量使用字符串和系统调用,为性能着想,你应该使用操作系统的本地字符集,这将是UTF-16在你的案件。
因此,对于你的字符串处理,你应该选择wchar_t
,在Windows的情况下意味着UTF16。不幸的是,wchar_t
的宽度可能因编译器而异;在Unix下它通常是UTF32,在Windows下是UTF16。
_MBCS
是一个自动的预处理器定义它告诉你,你已经定义你的角色设置为多字节,UNICODE
告诉你,你已经将它设置为UTF-16。
你甚至可以在一个程序中,这已经不是UNICODE
限定设置写
wchar_t* wcMsg = L"مرحبا";
MessageBoxW(0, wcMsg, 0, 0);
。前缀L"
定义了您的字符串是一个UNICODE
(宽字符)字符串,您可以使用它调用系统函数。
不幸的是,你可以不写
char* msg = u8"مرحبا";
MessageBoxA(0, msg, 0, 0);
的字符集的支持已经改进了C++ 11,所以你也可以通过前缀u8
定义字符串UTF8。但“A”后缀的窗口函数不理解UTF8。 (另请参阅https://stackoverflow.com/a/504789/2328447) 这也建议在Windows/Visual Studio下使用UTF16又名UNICODE。
设置你的项目为“使用多字节字符集”或“使用Unicode字符集”也改变了很多其他角色依赖定义:最常见的是宏TCHAR
,_T()
和所有的字符串相关的Windows功能,而不后缀,例如MessageBox()
(不W
或A
后缀) 如果设置您的项目以“使用多字节字符集”,TCHAR
将扩大到char
,_T()
将扩大到什么,和Windows功能将得到A
后缀连接。 如果您将项目设置为“使用Unicode字符集”,TCHAR
将扩展为wchar_t
,_T()
将扩展为L
前缀,并且Windows函数将获得附加的W
后缀。
这意味着,书写
TCHAR* msg = _T("Hello");
MessageBox(0, msg, 0, 0);
将与多字节字符集或Unicode集编译两者。你可以在MSDN找到关于这些主题的综合指南。
不幸的是
TCHAR* msg = _T("مرحبا");
MessageBox(0, msg, 0, 0);
仍然无法工作时,选择“使用多字节字符集” - 在Windows功能仍然不支持UTF8,你甚至会得到一些编译器警告,因为你定义了unicode字符,这些字符包含在未标记为Unicode的字符串中(_T()
未扩展为u8
)
年代学不太正确。 Unicode联盟最初使用16位字符集,推荐的编码是使用16位单元。这种编码被称为“Unicode”。 Windows遵循了建议,并将其编码称为“Unicode”。在Windows NT发布后很长时间,Unicode联盟直到1996年才转换为32位字符集。因此,在设计Windows时,调用编码为“Unicode”的16位字符是正确的。 (为了让事情变得更加混乱,Unicode联盟后来缩小为26位字符集。) –
噢好吧谢谢,然后我误会了。我会尽力解决这个问题。 – user2328447
(更正:Unicode目前是一个21位字符集,而不是26.有17个平面,每个平面都有65536个字符。) –
操作系统使用utf16编码的字符串作为其本机字符串类型。不使用wchar_t或std :: wstring足够用于“ASCII字符串”是程序员最终使用MultiByteToWideChar很多。这并不是特别错误,只是效率低下,但是用ASCII编码的字符串编写阿拉伯语当然没有什么希望。 C++和低效不应该是一个句子或程序中使用的两个单词。考虑使用像Qt这样的UI框架来减轻痛苦。 –
@HansPassant:谢谢。如果你添加一个答案呢?这将是非常有利的。 – WonFeiHong
“我习惯于处理ASCII字符串”:这非常可疑。如果您一直在调用MessageBoxA等Win32 API函数,读取或写入文本文件,读取或写入控制台或使用C +库,您一直在使用用户的“locale”,它指定了像CP437这样的字符编码,Windows-1252或类似的,几乎肯定不是ASCII。 –