Windows上MBCS和UTF-8之间的差异

Windows上MBCS和UTF-8之间的差异,windows,unicode,character-encoding,mbcs,Windows,Unicode,Character Encoding,Mbcs,我正在读有关Windows上的字符集和编码。我注意到VisualStudio编译器(用于C++)中有两个编译器标志,分别称为MBCS和UNICODE。他们之间有什么区别?我不明白的是UTF-8在概念上与MBCS编码有何不同?此外,我在中发现以下引用: Unicode是一种16位字符编码 这否定了我读到的关于Unicode的任何内容。我认为unicode可以使用不同的编码,比如UTF-8和UTF-16。有人能解释一下这种混淆吗?MBCS表示并描述任何字符集,其中字符编码(可能)超过1字节 ANSI

我正在读有关Windows上的字符集和编码。我注意到VisualStudio编译器(用于C++)中有两个编译器标志,分别称为MBCS和UNICODE。他们之间有什么区别?我不明白的是UTF-8在概念上与MBCS编码有何不同?此外,我在中发现以下引用:

Unicode是一种16位字符编码


这否定了我读到的关于Unicode的任何内容。我认为unicode可以使用不同的编码,比如UTF-8和UTF-16。有人能解释一下这种混淆吗?

MBCS表示并描述任何字符集,其中字符编码(可能)超过1字节

ANSI/ASCII字符集不是多字节的

然而,UTF-8是一种多字节编码。它将任何Unicode字符编码为1、2、3或4个八位字节(字节)的序列

然而,UTF-8只是Unicode字符集几种可能的具体编码中的一种。值得注意的是,UTF-16是另一个,它恰好是Windows/.NET(IIRC)使用的编码。以下是UTF-8和UTF-16之间的区别:

  • UTF-8将任何Unicode字符编码为1、2、3或4字节的序列

  • UTF-16将大多数Unicode字符编码为2字节,有些编码为4字节

因此,Unicode是16位字符编码是不正确的。它有点像21位编码(现在甚至更多),因为它包含一个字符集,其代码点
U+000000
高达
U+10FFFF

我注意到有两个编译器 Visual Studio编译器中的标志(用于 C++)称为MBCS和UNICODE。是什么 他们之间有什么区别

Windows API中的许多函数有两个版本:一个接受
char
参数(在特定于语言环境的代码页中),另一个接受
wchar\t
参数(在UTF-16中)

每个函数对都有一个不带后缀的宏,这取决于是否定义了
UNICODE

#ifdef UNICODE
   #define MessageBox MessageBoxW
#else
   #define MessageBox MessageBoxA
#endif
为了实现这一点,定义了
TCHAR
类型来抽象API函数使用的字符类型

#ifdef UNICODE
    typedef wchar_t TCHAR;
#else
    typedef char TCHAR;
#endif
然而,这。您应该始终明确指定字符类型

我不明白的是UTF-8是怎样的 概念上不同于MBCS 编码

MBCS代表“多字节字符集”。从字面上看,UTF-8似乎符合条件

但在Windows中,“MBCS”仅指可以和Windows API函数的“A”版本一起使用的字符编码。这包括代码页932(Shift_JIS)、936(GBK)、949(KS_C_5601-1987)和950(Big5),但不是UTF-8

要使用UTF-8,必须使用
MultiByteToWideChar
将字符串转换为UTF-16,调用函数的“W”版本,并在输出上调用
WideCharToMultiByte
。这基本上就是“A”函数的实际功能,这让我很好奇

由于无法提供支持,Windows API的“A”版变得毫无用处。因此,您应该始终使用“W”功能

Unicode是一种16位字符编码

这否定了我读到的关于 Unicode

MSDN是错误的。Unicode是一种21位编码字符集,具有多种编码,最常见的是UTF-8、UTF-16和UTF-32。(还有其他Unicode编码,如GB18030、UTF-7和UTF-EBCDIC。)


每当微软提到“Unicode”时,他们实际上是指UTF-16(或UCS-2)。这是出于历史原因。Windows NT是Unicode的早期采用者,当时16位被认为对每个人来说都足够了,UTF-8仅用于计划9。因此UCS-2是Unicode。MBCS和Unicode是决定调用哪个版本的TCHAR.H例程的宏。例如,如果使用
\u tcsclen
计算字符串的长度,预处理器将根据以下两个宏将
\u tcsclen
映射到不同的版本:\u MBCS和\u UNICODE

_UNICODE & _MBCS Not Defined: strlen  
_MBCS Defined: _mbslen  
_UNICODE Defined: wcslen  
为了解释这些字符串长度计数函数的差异,请考虑下面的例子。 如果您有一台运行使用GBK(936代码页)的Windows简体中文版的计算机,您可以编译一个GBK文件编码的源文件并运行它

printf("%d\n", _mbslen((const unsigned char*)"I爱你M"));
printf("%d\n", strlen("I爱你M"));
printf("%d\n", wcslen((const wchar_t*)"I爱你M"));
结果将是
463

以下是
I的十六进制表示形式爱你M
单位为GBK

GBK:             49 B0 AE C4 E3 4D 00                
_mbslen知道这个字符串是用GBK编码的,所以它可以正确地解释字符串并得到正确的结果
4
words:
49
as
I
B0 AE
as
C4 E3
as
4D
M

strlen只知道
0x00
,所以它得到
6

WCSCLIN将该十六进制数组编码在UTF16LE中,将两个字节作为一个字进行计数,得到<代码> 3 单词:<代码> 49 B0,<代码> AE C4,<代码> E34D。p>


正如@xiaokaoy所指出的,
wcslen
的唯一有效终止符是
00
。因此,如果以下字节不是
00

,则结果不能保证为
3
。作为其他答案的脚注,MSDN有一个带有方便表格的文档,总结了预处理器指令UNICODE和MBCS如何更改不同C/C++类型的定义

至于“Unicode”和“多字节字符集”的措辞,人们已经描述了其效果。我只想强调的是,这两个都是微软为一些非常具体的事情说话。(也就是说,如果对文本国际化的理解不是微软特有的,那么它们对Windows的意义就没有人们想象的那么普遍和特殊了。)这些确切的短语会出现,并且往往会有自己的separa
GBK:             49 B0 AE C4 E3 4D 00