Visual Studio字符集:'未设置'与'多字节字符集'

12

我一直在处理一个遗留应用程序,试图弄清楚使用 Multi byte character setNot Set 选项下编译的应用程序之间的区别。

我理解,使用 Multi byte character set 编译会定义 _MBCS,使多字节字符集代码页能够被使用,而使用 Not Set 则不会定义 _MBCS。在这种情况下,只允许使用单字节字符集代码页。

在使用 Not Set 的情况下,我假设我们只能使用此页面上找到的单字节字符集代码页:http://msdn.microsoft.com/en-gb/goglobal/bb964654.aspx

因此,如果使用 Not Set,该应用程序将无法对远东语言进行编码、写入或读取,因为它们是以双字节字符集代码页(当然还有 Unicode)定义的,我的理解正确吗?

接下来,如果定义了 Multi byte character set,那么单字节和多字节字符集代码页都可用,还是只有多字节字符集代码页可用?我猜测两者都可以支持欧洲语言。

谢谢,
Andy

进一步阅读

以下页面的答案没有回答我的问题,但有助于我理解:关于 Visual Studio 2010 中的“字符集”选项

研究

因此,只是作为工作研究...将我的区域设置为日语

对硬编码字符串的影响

char *foo = "Jap text: テスト";
wchar_t *bar = L"Jap text: テスト";

使用Unicode编译:

*foo = 4a 61 70 20 74 65 78 74 3a 20 83 65 83 58 83 67 == Shift-Jis (代码页932)
*bar = 4a 00 61 00 70 00 20 00 74 00 65 00 78 00 74 00 3a 00 20 00 c6 30 b9 30 c8 30 == UTF-16或UCS-2

使用多字节字符集编译:

*foo = 4a 61 70 20 74 65 78 74 3a 20 83 65 83 58 83 67 == Shift-Jis (代码页932)
*bar = 4a 00 61 00 70 00 20 00 74 00 65 00 78 00 74 00 3a 00 20 00 c6 30 b9 30 c8 30 == UTF-16或UCS-2

编译时未设置字符集:

*foo = 4a 61 70 20 74 65 78 74 3a 20 83 65 83 58 83 67 == Shift-Jis (代码页932)
*bar = 4a 00 61 00 70 00 20 00 74 00 65 00 78 00 74 00 3a 00 20 00 c6 30 b9 30 c8 30 == UTF-16或UCS-2

结论: 字符编码对硬编码字符串没有影响。虽然上述定义的char似乎使用了定义的本地代码页,而wchar_t似乎使用了UCS-2或UTF-16。

在Win32 API的W/A版本中使用编码字符串

因此,使用以下代码:

char *foo = "C:\\Temp\\テスト\\テa.txt";
wchar_t *bar = L"C:\\Temp\\テスト\\テw.txt";

CreateFileA(bar, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
CreateFileW(foo, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

使用 Unicode 进行编译:

结果:两个文件都被创建

使用 多字节字符集 进行编译:

结果:两个文件都被创建

使用 未设置 进行编译:

结果:两个文件都被创建

结论: 无论选择哪种字符集,API 的 A 版本和 W 版本都期望相同的编码。因此,我们可以假设 字符集 选项只是在 API 版本之间进行切换。因此,A 版本始终期望当前代码页编码的字符串,而 W 版本始终期望 UTF-16 或 UCS-2 编码。

使用 W 和 A Win32 APIs 打开文件

因此,可以使用以下代码:

char filea[MAX_PATH] = {0};
OPENFILENAMEA ofna = {0};
ofna.lStructSize = sizeof ( ofna );
ofna.hwndOwner = NULL  ;
ofna.lpstrFile = filea ;
ofna.nMaxFile = MAX_PATH;
ofna.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
ofna.nFilterIndex =1;
ofna.lpstrFileTitle = NULL ;
ofna.nMaxFileTitle = 0 ;
ofna.lpstrInitialDir=NULL ;
ofna.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST ;  

wchar_t filew[MAX_PATH] = {0};
OPENFILENAMEW ofnw = {0};
ofnw.lStructSize = sizeof ( ofnw );
ofnw.hwndOwner = NULL  ;
ofnw.lpstrFile = filew ;
ofnw.nMaxFile = MAX_PATH;
ofnw.lpstrFilter = L"All\0*.*\0Text\0*.TXT\0";
ofnw.nFilterIndex =1;
ofnw.lpstrFileTitle = NULL;
ofnw.nMaxFileTitle = 0 ;
ofnw.lpstrInitialDir=NULL ;
ofnw.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST ;

GetOpenFileNameA(&ofna);
GetOpenFileNameW(&ofnw);

并选择其中之一:

  • C:\Temp\テスト\テopenw.txt
  • C:\Temp\テスト\テopenw.txt

结果为:

当使用Unicode编译时

*filea = 43 3a 5c 54 65 6d 70 5c 83 65 83 58 83 67 5c 83 65 6f 70 65 6e 61 2e 74 78 74 == Shift-Jis(代码页932)
*filew = 43 00 3a 00 5c 00 54 00 65 00 6d 00 70 00 5c 00 c6 30 b9 30 c8 30 5c 00 c6 30 6f 00 70 00 65 00 6e 00 77 00 2e 00 74 00 78 00 74 00 == UTF-16或UCS-2

当使用Multi byte character set编译时

*filea = 43 3a 5c 54 65 6d 70 5c 83 65 83 58 83 67 5c 83 65 6f 70 65 6e 61 2e 74 78 74 == Shift-Jis(代码页932)
*filew = 43 00 3a 00 5c 00 54 00 65 00 6d 00 70 00 5c 00 c6 30 b9 30 c8 30 5c 00 c6 30 6f 00 70 00 65 00 6e 00 77 00 2e 00 74 00 78 00 74 00 == UTF-16或UCS-2

当使用Not Set编译时

*filea = 43 3a 5c 54 65 6d 70 5c 83 65 83 58 83 67 5c 83 65 6f 70 65 6e 61 2e 74 78 74 == Shift-Jis (Code page 932)
*filew = 43 00 3a 00 5c 00 54 00 65 00 6d 00 70 00 5c 00 c6 30 b9 30 c8 30 5c 00 c6 30 6f 00 70 00 65 00 6e 00 77 00 2e 00 74 00 78 00 74 00 == UTF-16 or UCS-2

结论: 再次强调,字符集设置与Win32 API的行为无关。 A版本似乎始终返回具有活动代码页编码的字符串,而W则始终返回UTF-16或UCS-2编码。我在这个很好的答案中实际上可以看到这种解释: https://dev59.com/L3A75IYBdhLWcg3wbojM#3299860.

最终结论

当Hans说到定义实际上没有任何魔力时,他似乎是正确的,除了将Win32 API更改为使用WA之外,它并没有发生什么变化。因此,我无法真正看到未设置多字节字符集之间的任何区别。

2个回答

8
不,实际情况并非如此。宏只是被定义,对编译器没有任何魔力影响。很少有人真正写出使用 #ifdef _MBCS 测试此宏的代码。
你几乎总是让辅助函数来完成转换,例如 WideCharToMultiByte()、OLE2A() 或 wctombs(),这些转换函数总是考虑多字节编码,并受代码页指导。_MBCS 是一个历史偶然,仅在 25 年前多字节编码尚不普遍时才相关。就像现在使用非 Unicode 编码一样,这也是一个历史遗物。

所以如果我理解正确,如果我定义一个硬编码的字符串,例如char *foo = "テスト"。那么foo指向的字符串并不是由字符集设置定义的?也许是包含该行的代码文件的编码方式?(我目前正在尝试测试这些理论) - Andy
这将强制您的文本编辑器为源代码文件选择适当的编码方式。这本身就是一个意外的来源。如果它选择了Unicode编码,utf-8是常见的,那么您可能会让编译器对此感到不满。在我的机器上是C4566。只有在您住在日本并且不考虑很快搬家时才考虑像这样编写它。 - Hans Passant
好的,听起来我现在对这个有点理解了。宏定义实际上并没有什么作用,代码页是在机器上设置的,不管应用程序如何编译,而宏定义只是改变Win32 API的基础,根据它是W还是A,我猜它会返回页面代码(多字节或单字节字符集)编码的东西(A)或UTF-16(W)? - Andy

0

参考文献中指出:

根据定义,ASCII字符集是所有多字节字符集的子集。在许多多字节字符集中,范围为0x00-0x7F的每个字符与ASCII字符集中具有相同值的字符相同。例如,在ASCII和MBCS字符字符串中,1字节的NULL字符('\ 0')的值为0x00,并表示终止空字符。

正如您所猜测的那样,通过启用_MBCS,Visual Studio还支持ASCII单字符集。

在第二个参考文献中,即使我们启用_MBCS,也似乎支持单字符集:

MBCS/Unicode可移植性:使用Tchar.h头文件,您可以从相同的源代码构建单字节、MBCS和Unicode应用程序。Tchar.h定义了以_tcs为前缀的宏,这些宏映射到适当的str、_mbs或wcs函数。要构建MBCS,请定义符号_MBCS。要构建Unicode,请定义符号_UNICODE。对于MFC应用程序,默认情况下定义了_MBCS。有关更多信息,请参阅Tchar.h中的通用文本映射。

但是如果不使用_MBCS,那么API会根据区域设置使用基于单字节字符集代码页的编码方式,比如在以下网址上定义的编码方式:http://msdn.microsoft.com/en-gb/goglobal/bb964654.aspx?所以,这些编码方式都以ASCII范围为开头,但它们还定义了其他外文字符。 - Andy
@Andy,是的,ASCII是一个7位字符集,包含128个字符,而单字节(8位)本地编码可以编码256个字符。 - fatihk
是的,问题仍然存在,如果定义了MBCS,那么单字节字符集代码页是否被排除在外(因此泰语字符呢)?如果我没有使用MBCS进行编译,我猜测应用程序将无法处理远东字符,因为它受限于单字节字符集代码页。 - Andy
@Andy,根据第二个参考资料,似乎是被支持的。 - fatihk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接