为什么我们从MultiByte转换为WideChar?

时间:2017-11-11 12:05:10

标签: c++ windows unicode

我习惯处理ASCII字符串但是现在使用UNICODE我对某些术语太过困惑了:

什么是多字节字符,什么是alphabet有什么区别? 多字节是指内存中包含多个字节的字符,而widechar只是表示它的数据类型吗?

  • 为什么我们转换自widecharMultiByteToWideChar

如果我宣布这样的话:

WideCharToMultiByte

如果我定义了wchar_t* wcMsg = L"مرحبا"; MessageBoxW(0, wcMsg, 0, 0); ,它会正确打印消息但是为什么我没有从UNICODE转换到这里

  • 我的项目中的字符集有什么区别:WideCharToMultiByte_MBCS

  • MSDN让我误以为“Windows API”的最后一件事是UTF-16。

任何人都可以用一些例子来解释。非常感谢您的澄清。

1 个答案:

答案 0 :(得分:4)

ASCII字符串的字符宽度为一个字节(通常为8位,很少为7,9或其他位宽)。这是当时的遗留问题,当内存大小非常小且昂贵时,处理器通常每条指令只能处理一个字节。

由于很容易想象,到目前为止,一个字节还不足以存储世界上所有可用的字形。仅中国就有87,000个字形。 char通常只能处理256个字形(以8位字节为单位)。 ASCII仅定义了96个字形(加上较低的32个字符,它们被定义为不可打印的控制字符)。这对于英语上下字符,数字以及一些插值和其他字形来说已经足够了。

要处理比一个字节可以容纳的字形更多的字形,一种方法是将基本字形存储在一个字节中,将其他常见字形存储在两个字节中,并且很少使用3个甚至更多字节中的字形。这种方法称为Multi byte char set or Variable-width encoding。一个非常常见的例子是UTF 8,它对一个字符使用一个最多4个字节。它将ASCII字符集存储在一个字节中(因此它也向后兼容ASCII)。最高位被定义为一个开关:如果它被设置,其他字节将跟随。这同样适用于以下字节,因此"链"形成最多4个字节。 可变宽度字符集的专业人员是:

  • 向后兼容8位ASCII字符集
  • 内存友好 - 使用尽可能少的内存

缺点是:

  • 处理起来更困难且处理器更昂贵。你不能简单地迭代一个字符串并假设每个myString[n]提供一个字形;相反,如果跟随更多字节,则必须评估每个字节。

另一种方法是将每个字符存储在由n个字节组成的固定长度字中,该字宽足以容纳所有可能的字形。这称为固定宽度字符集;所有字符都有相同的宽度。一个众所周知的例子是UTF32。它是32位宽,可以在一个单词中存储所有可能的字符。固定宽度字符集的专业人员和内容显然与可变宽度字符集相反:内存繁重但更容易迭代。

但是,即使在UTF32可用之前,微软也选择了他们的原生字符集:他们使用UTF16作为Windows的字符集,它使用至少2个字节(16位)的字长。这足以存储比单字节字符集更多的字形,但不是全部。考虑到这一点,微软在多字节"多字节"之间进行区分。和" Unicode"今天有点误导,因为它们的unicode实现也是一个多字节字符集 - 只有一个字形的最小大小。有人说这是一个很好的妥协,有人说它是两个世界中最糟糕的 - 无论如何,这就是它的方式。那时(Windows NT)它是唯一可用的Unicode字符集,从这个角度来看,它们在多字符和Unicode之间的区别当时是正确的(参见Raymond Chen的评论)

当然,如果你想将一个字符串中的字符串(让我们说UTF8)转换成另一个字符串(让我们说UTF16),你必须转换它们。这是MultiByteToWideChar为你做的事情,WideCharToMultiByte反之亦然。还有一些其他的转换函数和库。

这种转换花费了相当多的时间,因此结论是:如果你大量使用字符串和系统调用,为了性能,你应该使用操作系统的本机字符集,在你的情况下是UTF16

因此,对于字符串处理,您应该选择wchar_t,如果是Windows则表示UTF16。不幸的是,wchar_t的宽度可能因编译器而异;在Unix下它通常是UTF32,在Windows下它是UTF16。

_MBCS是一个自动预处理器定义,它告诉您已将字符集定义为多字节,UNICODE告诉您已将其设置为UTF16。

你可以写

wchar_t* wcMsg = L"مرحبا";
MessageBoxW(0, wcMsg, 0, 0);

即使在没有UNICODE定义集的程序中也是如此。 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 aka UNICODE。

将项目设置为"使用多字节字符集"或"使用Unicode字符集"还会更改许多其他字符相关定义:最常见的是宏TCHAR_T()以及所有不依赖于后缀的字符串相关Windows函数,例如: MessageBox()(没有WA后缀) 如果您将项目设置为"使用多字节字符集",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