为什么我们从MultiByte转换为WideChar

Why we convert from MultiByte to WideChar?

本文关键字:WideChar 转换 MultiByte 我们 为什么      更新时间:2023-10-16

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

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

  • 为什么要从MultiByteToWideCharWideCharToMultiByte转换

如果我声明这样的东西:

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

如果我定义了UNICODE,它会正确打印消息。但为什么我没有从WideCharToMultiByte转换到这里??

  • 我的项目中的字符集_MBCSUNICODE之间有什么区别?

  • MSDN让我对"Windows API"感到困惑的最后一件事是UTF-16。

有人能举例说明吗。非常感谢您的澄清。

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

很容易想象,到目前为止,一个字节还不足以存储世界上所有可用的字形。仅中文就有87.000个字形。一个字符通常只能处理256个字形(在一个8位字节中)。ASCII只定义了96个字形(加上下面的32个字符,这些字符被定义为不可打印的控制字符),这使它成为一个7位字符集。这对于英语的上下字符、数字、一些标点符号和其他字形来说已经足够了。ASCII不使用公共8位字节中的最高位。

要处理超过一个字节所能容纳的字形,一种方法是将基本字形存储在一个字节中,将其他常见字形存储在两个字节中;将很少使用的字形存储在3个甚至更多个字节中。这种方法被称为多字节字符集可变宽度编码。一个非常常见的例子是UTF 8,它为一个字符使用1到4个字节。它将ASCII字符集存储在一个字节中(因此它也向后兼容ASCII)。最高的位被定义为一个开关:如果设置了它,其他字节将紧随其后。这同样适用于以下字节,从而形成最多4个字节的"链"。可变宽度字符集的优点是:

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

缺点是:

  • 处理起来更困难,处理器成本更高。您不能简单地迭代一个字符串并假设每个myString[n]提供一个字形;相反,如果后面有更多字节,则必须计算每个字节

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

但微软甚至在UTF32问世之前就选择了他们的原生字符集:他们使用UTF16作为Windows的字符集,Windows使用至少2字节(16位)的字长。这足够大,可以存储比单字节字符集多得多的字形,但不是所有的字形。考虑到这一点,微软对"多字节"answers"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"后缀的windows函数不理解UTF8,至少在windows 10 Build 17035之前是这样(参见tambre的评论)(另请参见https://stackoverflow.com/a/504789/2328447)这也建议在Windows/Vistudio下使用UTF16,也就是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)