user-0 提问: C和win32的宽字符是否就是UTF-16？

回复:
user: user-0
content: 引用 7 楼 Intel0011 的回复:1、目前使用4个字节来编码字符足以，Unicode的总量约在110万个字符，现在使用的也就20万左右（不记得在哪看过，不知对否），常用的用两字节存储，不常用的用4字节 2、如你所说，VC10编译器，wchar_t的编码方式是UTF-16LE，GCC下没测试 3、gcc -finput-charset=GBK -fexec-charset=GBK XXX.c  半个月了都，我总算是搞明白了，我把成果跟你说下吧，如果你知道的话，就顺便指个错吧，否则就当查漏补缺吧 关于chs那个，我通过修改系统的设置，然后再查看当前环境名称就得到了，中文简体是Chinese (Simplified)_China.936，语言部分简写可以简写chs、chi等，繁体是Chinese (Traditional)，可简写cht  gcc参数那个，gcc的默认参数（Windows）应该是代码文件-finput-charset=UTF-8，窄字符串-fexec-charset=UTF-8，宽字符串-fwide-exec-charset=UTF16LE，标注的代码文件的编码和窄字符串的编码相同，所以窄字符串没有转码，宽字符串则从UTF-8转到UTF-16LE，但是实际写代码用的GBK（dev），所以汉字的值在UTF-8里就成了错误值，gcc停止编译，-finput-charset=GBK让告诉gcc代码是GBK，于是gcc窄宽字符串分别从GBK->UTF-8、GBK->UTF-16LE。  然后就是printf等函数，会根据控制台的编码来转码，但是需要知道源码和目标编码，它默认的源码是ASCII，超出ASCII的部分应该是无法或没有转码，因此窄字符串的输出是直接给原值 system("chcp 54936"); char str[10]={ 0x81, 0x39, 0xEF, 0x31 }; printf("%s", str ); 这个也没经过转码，该GB18030的编码给了用GB18030的控制台，于是正确显示 而平时控制台的编码是GBK，所以窄字符串从GBK转到UTF-8后，用不会转换的printf输出，然后就乱码了，这时system("chcp 65001")调到一致的UTF-8就可以避免乱码  如果是让printf等函数正常跟随控制台转码，就用setlocale设置，它会设置窄字符串的编码和宽字符串的编码（源），Windows上setlocale一般设不了UTF-8，如果设GBK，就表示printf要将作为源的GBK窄字符串转码到控制台的目标编码，以及UTF-16LE的宽字符串转码到控制台，之前经gcc转码，宽字符串已经是UTF-16LE，但窄字符串是UTF-8，所以用-fexec-charset=GBK让它再转码到GBK，printf函数就能正常转码了，这时修改控制台编码也不会乱码了（除非控制台的编码没有该字符）  之前查好久楞是没怎么明白setlocale的意思，现在想来应该是指示exe中字符串的编码，但表面上设置的是窄编码，实际还隐含设置了宽编码，以及printf输出还有转码功能，没想到这些

user: user-0
content: 引用 7 楼 Intel0011 的回复:1、目前使用4个字节来编码字符足以，Unicode的总量约在110万个字符，现在使用的也就20万左右（不记得在哪看过，不知对否），常用的用两字节存储，不常用的用4字节 2、如你所说，VC10编译器，wchar_t的编码方式是UTF-16LE，GCC下没测试 3、gcc -finput-charset=GBK -fexec-charset=GBK XXX.c  又遇到了一些问题你看看了解吗  一个比较简单的问题是，你知道setlocale的"chs"是哪来的吗？微软官网给的是zh_CN，没看到chs  关于gcc -finput-charset=GBK的这个一直没想通，网上说gcc默认UTF-8的代码，但是我GBK的代码文件依然正常编译输出，我猜测是它没有处理，直接把这些字节值编译了，函数最后把这些字节值给了控制台，控制台根据得到的字节值正确显示。而宽字符串L字面量，需要gcc编译时转换为UTF-16LE（发现是编译到汇编码这一步翻译的），这时我用-finput-charset=GBK 告诉它代码是GBK的，于是他正确转换了，但问题也来了，原本的窄字符串由于这个参数输出变成乱码了，显然gcc处理了这个字符串，查看汇编码，它转成了UTF-8，会什么会这样？？之前明明没处理的。还是说，正因为知道了都是GBK，所以它要转换？ -fexec-charset=GBK这个我应该理解了，它应该是又把UTF-8转成了GBK，然后程序给控制台GBK值，于是控制台正确显示。  如果按照前面的理解，那setlocale是不是就成了设置最终目标转码方案的了（-fexec-charset生成到exe的，setlocale指定exe到控制台的编码）？毕竟窄字符串原样给控制台，而宽字符串字面量先从参数告知的GBK转为UTF-16，后又从UTF-16转为setlocale设置的GBK，发给控制台，于是正确显示，但问题也来了，既然告知了代码的编码，以及目标的编码，为啥普通字符串却乱码了？是我对setlocale的理解有误吗？  我用system("chcp 54936") 设置控制台显示编码为GB18030，setlocale( LC_ALL, "zh_CN.54936" ) 也设置了GB18030，-fexec-charset=GB18030参数也指定转换目标，但是L宽字符串没有显示出来。此时我用指定字节的方式指定一个GB18030的字符输出： char str[10]={ 0x81, 0x39, 0xEF, 0x31 }; printf("%s", str ); 能正确输出该汉字 然后修改setlocale( LC_ALL, "chs" ) ，该汉字乱码了，但是宽字符串却输出了 这又是一个疑惑的地方，"chs" 按理说是Chinese Simplified，不应该等同于"zh_CN.936"吗？可是设置setlocale( LC_ALL, "zh_CN.936" ) ，却依然像之前那样，该汉字正确输出，宽字符串无输出  不知道是哪里理解错了 代码的编码——gcc转换——exe里的字节值——函数根据环境的输出——在控制台上的显示

user: user-0
content: 引用 7 楼 Intel0011 的回复:1、目前使用4个字节来编码字符足以，Unicode的总量约在110万个字符，现在使用的也就20万左右（不记得在哪看过，不知对否），常用的用两字节存储，不常用的用4字节 2、如你所说，VC10编译器，wchar_t的编码方式是UTF-16LE，GCC下没测试 3、gcc -finput-charset=GBK -fexec-charset=GBK XXX.c  感谢！感谢！

user: user-1
content: 引用 6 楼 六道佩恩 的回复:谢谢你回答这么多 不过看了后我反而有些新的问题了  UTF-16这个编码方式是不是只能保存一百万左右的字符量？以后超出是不是还得用6字节？这个编码方式我感觉还是多字节的那一套，前导字在某个范围时，联合第二个字一起作为一个UNICODE字符，它为什么不将第二个字的使用范围扩大？这么做是不是有什么考量？比如容错？  这句话是不是有问题：“VC10编译器，wchar_t的编码方式是UTF-16BE，而在GCC下是UTF-32BE” Windows上不都是小端序LE吗？而且这个GCC是不是也要看平台？比如Windows平台的GCC测出的是2字节。  网上看到这样一句话：UTF-32编码被提出来了，它的编码值与UCS-4相同，只不过其编码空间被限定在了0~0x10FFFF之间 意思是UTF-32也只保存百万左右的字符数？超过百万也要变长？  我GCC编码宽字符串遇到一个问题，-finput-charset=GBK可以让宽字符串被正确编译和输出，但是普通的字符串却乱码了，你知道是什么原因吗？当前的编写环境是GBK（dev下），setlocale是setlocale( LC_ALL, "chs"); 1、目前使用4个字节来编码字符足以，Unicode的总量约在110万个字符，现在使用的也就20万左右（不记得在哪看过，不知对否），常用的用两字节存储，不常用的用4字节 2、如你所说，VC10编译器，wchar_t的编码方式是UTF-16LE，GCC下没测试 3、gcc -finput-charset=GBK -fexec-charset=GBK XXX.c

user: user-0
content: 引用 5 楼 Intel0011 的回复:首先，UTF-16并不是固定2字节，UCS-2是固定2字节 Windows平台下默认的Unicode编码为Little Endian的UTF-16 C语言中的wchar_t类型的宽度取决于C实现，在有的平台上它是16位的，而在另一些平台上它是32位的，所以，C11引入了两种更加明确的类型：char16_t和char32_t。 它们是在头文件<uchar.h>中定义的，它们都是无符号整数类型，前者的宽度保证是16个比特，后者的宽度保证是32个比特。 因此，宽字符既可以用wchar_t类型表示，也可以用char16_t或char32_t类型表示。 如果宽字符是用wchar_t类型表示的，则它的编码方式取决于C实现； 如果宽字符是用char16_t类型表示的，且C实现预定义了值为1的宏__STD_UTF_16__，则宽字符的编码方式是UTF-16，如果未定义这个宏，则宽字符的实际编码方式取决于C实现。 如果宽字符是用char32_t类型表示的，且C实现预定义了值为1的宏__STD_UTF_32__，则宽字符的编码方式是UTF-32，如果未定义这个宏，则宽字符的实际编码方式取决于C实现。 VC10编译器，wchar_t的编码方式是UTF-16BE，而在GCC下是UTF-32BE。 wchar_t的目的是编码并存储所有字符集，编码方式和存储空间大小和语言无关，只和编译器有关，因此说wchar_t的编码方式是Unicode是错的。  1990年就公布了第一套编码方法UCS-2，使用2个字节表示已经有码点的字符。 (那个时候只有一个平面，就是基本平面，所以2个字节就够用了) 但俩个字节并不足以正真地"一统江湖"(a fixed-width 2-byte encoding could not encode enough characters to be truly universal)， 于是UTF-16诞生了，与UCS-2一样，它使用两个字节为全世界最常用的63K字符编码，不同的是，它使用4个字节对不常用的字符进行编码。UTF-16属于变长编码。  UTF-16编码迟至1996年7月才公布，明确宣布是UCS-2的超集，即基本平面字符沿用UCS-2编码，辅助平面字符定义了4个字节的表示方法。 两者的关系简单说，就是UTF-16取代了UCS-2，或者说UCS-2整合进了UTF-16。所以，现在只有UTF-16，没有UCS-2。  UTF-16 UFT-16比较奇葩，它使用2个或者4个字节来存储。 BMP平面(也就是前面说的基本多文种平面)中的每一个码点都直接与一个UTF-16的码元一一映射。 由于BMP几乎包括了所有常见字符，UTF-16一般需要UTF-32大约一半的空间。至于其它平面里很少使用的码点都是用两个16位的码元来编码的。  对于Unicode编号范围在0~FFFF之间的字符，UTF-16使用两个字节存储，并且直接存储Unicode编号，不用进行编码转换，这跟UTF-32非常类似。 对于Unicode编号范围在10000~10FFFF之间的字符，UTF-16使用四个字节存储， 具体来说就是：将字符编号的所有比特位分成两部分，较高的一些比特位用一个值介于D800~DBFF之间的双字节存储， 较低的一些比特位(剩下的比特位)用一个值介于DC00~DFFF之间的双字节存储。 如果你不理解什么意思，请看下面的表格：  Unicode 编号范围(十六进制)      具体的Unicode编号(二进制)    UTF-16编码                                                 编码后的字节数 0000 0000 ~ 0000 FFFF	         xxxxxxxx xxxxxxxx	               xxxxxxxx xxxxxxxx	                                     2 0001 0000 ~ 0010 FFFF	         yyyy yyyy yyxx xxxx xxxx      110110yy yyyyyyyy 110111xx xxxxxxxx 	 4 位于D800~0xDBFF之间的Unicode编码是特别为四字节的UTF-16编码预留的，所以不应该在这个范围内指定任何字符。 如果你真的去查看Unicode字符集，会发现这个区间内确实没有收录任何字符。 UTF-16要求在制定Unicode字符集时必须考虑到编码问题，所以真正的Unicode字符集也不是随意编排字符的。  UTF-16并不是一个完美的选择，它存在几个方面的问题： 1. UTF-16能表示的字符数有6万多，看起来很多，但是实际上目前Unicode5.0收录的字符已经达到99024个字符，早已超过UTF-16的存储范围；     这直接导致UTF-16地位颇为尴尬——如果谁还在想着只要使用UTF-16就可以高枕无忧的话，恐怕要失望了。---> 这里实际上说的是UCS-2 2. UTF-16存在大小端字节序问题，这个问题在进行信息交换时特别突出——如果字节序未协商好，将导致乱码；     如果协商好，但是双方一个采用大端一个采用小端，则必然有一方要进行大小端转换，性能损失不可避免     (大小端问题其实不像看起来那么简单，有时会涉及硬件、操作系统、上层软件多个层次，可能会进行多次转换) 3. 另外，容错性低有时候也是一大问题——局部的字节错误，特别是丢失或增加可能导致所有后续字符全部错乱，     错乱后要想恢复，可能很简单，也可能会非常困难。(这一点在日常生活里大家感觉似乎无关紧要，但是在很多特殊环境下却是巨大的缺陷)  目前支撑我们继续使用UTF-16的理由主要是考虑到它是双字节的，在计算字符串长度、执行索引操作时速度很快。 当然这些优点UTF-32都具有，但很多人毕竟还是觉得UTF-32太占空间了。  谢谢你回答这么多 不过看了后我反而有些新的问题了  UTF-16这个编码方式是不是只能保存一百万左右的字符量？以后超出是不是还得用6字节？这个编码方式我感觉还是多字节的那一套，前导字在某个范围时，联合第二个字一起作为一个UNICODE字符，它为什么不将第二个字的使用范围扩大？这么做是不是有什么考量？比如容错？  这句话是不是有问题：“VC10编译器，wchar_t的编码方式是UTF-16BE，而在GCC下是UTF-32BE” Windows上不都是小端序LE吗？而且这个GCC是不是也要看平台？比如Windows平台的GCC测出的是2字节。  网上看到这样一句话：UTF-32编码被提出来了，它的编码值与UCS-4相同，只不过其编码空间被限定在了0~0x10FFFF之间 意思是UTF-32也只保存百万左右的字符数？超过百万也要变长？  我GCC编码宽字符串遇到一个问题，-finput-charset=GBK可以让宽字符串被正确编译和输出，但是普通的字符串却乱码了，你知道是什么原因吗？当前的编写环境是GBK（dev下），setlocale是setlocale( LC_ALL, "chs");

user: user-2
content: 在目前的windows语境下，是。

user: user-1
content: 引用 楼主 六道佩恩 的回复:UTF-8、多字节编码都是8位编码，UTF-32又是4字节，固定2字节一个字符的只想到了UTF-16和UCS-2，但UCS-2的字符集又不全，是不是说，C和win32的宽字符是否就是UTF-16？ 首先，UTF-16并不是固定2字节，UCS-2是固定2字节 Windows平台下默认的Unicode编码为Little Endian的UTF-16 C语言中的wchar_t类型的宽度取决于C实现，在有的平台上它是16位的，而在另一些平台上它是32位的，所以，C11引入了两种更加明确的类型：char16_t和char32_t。 它们是在头文件<uchar.h>中定义的，它们都是无符号整数类型，前者的宽度保证是16个比特，后者的宽度保证是32个比特。 因此，宽字符既可以用wchar_t类型表示，也可以用char16_t或char32_t类型表示。 如果宽字符是用wchar_t类型表示的，则它的编码方式取决于C实现； 如果宽字符是用char16_t类型表示的，且C实现预定义了值为1的宏__STD_UTF_16__，则宽字符的编码方式是UTF-16，如果未定义这个宏，则宽字符的实际编码方式取决于C实现。 如果宽字符是用char32_t类型表示的，且C实现预定义了值为1的宏__STD_UTF_32__，则宽字符的编码方式是UTF-32，如果未定义这个宏，则宽字符的实际编码方式取决于C实现。 VC10编译器，wchar_t的编码方式是UTF-16BE，而在GCC下是UTF-32BE。 wchar_t的目的是编码并存储所有字符集，编码方式和存储空间大小和语言无关，只和编译器有关，因此说wchar_t的编码方式是Unicode是错的。  1990年就公布了第一套编码方法UCS-2，使用2个字节表示已经有码点的字符。 (那个时候只有一个平面，就是基本平面，所以2个字节就够用了) 但俩个字节并不足以正真地"一统江湖"(a fixed-width 2-byte encoding could not encode enough characters to be truly universal)， 于是UTF-16诞生了，与UCS-2一样，它使用两个字节为全世界最常用的63K字符编码，不同的是，它使用4个字节对不常用的字符进行编码。UTF-16属于变长编码。  UTF-16编码迟至1996年7月才公布，明确宣布是UCS-2的超集，即基本平面字符沿用UCS-2编码，辅助平面字符定义了4个字节的表示方法。 两者的关系简单说，就是UTF-16取代了UCS-2，或者说UCS-2整合进了UTF-16。所以，现在只有UTF-16，没有UCS-2。  UTF-16 UFT-16比较奇葩，它使用2个或者4个字节来存储。 BMP平面(也就是前面说的基本多文种平面)中的每一个码点都直接与一个UTF-16的码元一一映射。 由于BMP几乎包括了所有常见字符，UTF-16一般需要UTF-32大约一半的空间。至于其它平面里很少使用的码点都是用两个16位的码元来编码的。  对于Unicode编号范围在0~FFFF之间的字符，UTF-16使用两个字节存储，并且直接存储Unicode编号，不用进行编码转换，这跟UTF-32非常类似。 对于Unicode编号范围在10000~10FFFF之间的字符，UTF-16使用四个字节存储， 具体来说就是：将字符编号的所有比特位分成两部分，较高的一些比特位用一个值介于D800~DBFF之间的双字节存储， 较低的一些比特位(剩下的比特位)用一个值介于DC00~DFFF之间的双字节存储。 如果你不理解什么意思，请看下面的表格：  Unicode 编号范围(十六进制)      具体的Unicode编号(二进制)    UTF-16编码                                                 编码后的字节数 0000 0000 ~ 0000 FFFF	         xxxxxxxx xxxxxxxx	               xxxxxxxx xxxxxxxx	                                     2 0001 0000 ~ 0010 FFFF	         yyyy yyyy yyxx xxxx xxxx      110110yy yyyyyyyy 110111xx xxxxxxxx 	 4 位于D800~0xDBFF之间的Unicode编码是特别为四字节的UTF-16编码预留的，所以不应该在这个范围内指定任何字符。 如果你真的去查看Unicode字符集，会发现这个区间内确实没有收录任何字符。 UTF-16要求在制定Unicode字符集时必须考虑到编码问题，所以真正的Unicode字符集也不是随意编排字符的。  UTF-16并不是一个完美的选择，它存在几个方面的问题： 1. UTF-16能表示的字符数有6万多，看起来很多，但是实际上目前Unicode5.0收录的字符已经达到99024个字符，早已超过UTF-16的存储范围；     这直接导致UTF-16地位颇为尴尬——如果谁还在想着只要使用UTF-16就可以高枕无忧的话，恐怕要失望了。---> 这里实际上说的是UCS-2 2. UTF-16存在大小端字节序问题，这个问题在进行信息交换时特别突出——如果字节序未协商好，将导致乱码；     如果协商好，但是双方一个采用大端一个采用小端，则必然有一方要进行大小端转换，性能损失不可避免     (大小端问题其实不像看起来那么简单，有时会涉及硬件、操作系统、上层软件多个层次，可能会进行多次转换) 3. 另外，容错性低有时候也是一大问题——局部的字节错误，特别是丢失或增加可能导致所有后续字符全部错乱，     错乱后要想恢复，可能很简单，也可能会非常困难。(这一点在日常生活里大家感觉似乎无关紧要，但是在很多特殊环境下却是巨大的缺陷)  目前支撑我们继续使用UTF-16的理由主要是考虑到它是双字节的，在计算字符串长度、执行索引操作时速度很快。 当然这些优点UTF-32都具有，但很多人毕竟还是觉得UTF-32太占空间了。

user: user-3
content: 仅供参考：#pragma comment(lib,"user32") #pragma comment(lib,"gdi32") #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> extern "C" HWND WINAPI GetConsoleWindow(); void HideTheCursor() {     CONSOLE_CURSOR_INFO cciCursor;     HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);      if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {         cciCursor.bVisible = FALSE;         SetConsoleCursorInfo(hStdOut, &cciCursor);     } } void ShowTheCursor() {     CONSOLE_CURSOR_INFO cciCursor;     HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);      if(GetConsoleCursorInfo(hStdOut, &cciCursor)) {         cciCursor.bVisible = TRUE;         SetConsoleCursorInfo(hStdOut, &cciCursor);     } } int main() {     HWND  hwnd;     HDC   hdc;     HFONT hfont;     wchar_t wc[2];      system("color F0");     system("cls");     HideTheCursor();     hwnd  = GetConsoleWindow();     hdc   = GetDC(hwnd);     hfont = CreateFont(48,0,0,0,0,0,0,0,GB2312_CHARSET ,0,0,0,0,"宋体-方正超大字符集");     SelectObject(hdc,hfont);     wc[0]=0xD854u;     wc[1]=0xDC00u;     TextOutW(hdc,10,10,wc,2);     DeleteObject(hfont);     ReleaseDC(hwnd,hdc);     getch();     system("color 07");     system("cls");     ShowTheCursor();     return 0; } #if 0 代理项或代理项对是一对共同表示单个字符的 16 位 Unicode 编码值。需要记住的关键一点是： 代理项对实际上是 32 位单个字符，不能再假定一个 16 位 Unicode 编码值正好映射到一个字符。  使用代理项对 代理项对的第一个值是高代理项，包含介于 U+D800 到 U+DBFF 范围内的 16 位代码值。 该对的第二个值是低代理项，包含介于 U+DC00 到 U+DFFF 范围内的值。通过使用代理项对， 16 位 Unicode 编码系统可以对已由 Unicode 标准定义的一百多万个其他字符 (220) 进行寻址。  在传递给 XmlTextWriter 方法的任何字符串中都可以使用代理项字符。不过，代理项字符在编写的 XML 中应该有效。例如，万维网联合会 (W3C) 建议不允许在元素或属性的名称中使用代理项字符。 如果字符串包含无效的代理项对，则引发异常。  另外，可以使用 WriteSurrogateCharEntity 写出与代理项对相对应的字符实体。字符实体以十六 进制格式写出，并用以下公式生成：  (highChar -0xD800) * 0x400 + (lowChar -0xDC00) + 0x10000  如果字符串包含无效的代理项对，则引发异常。下面的示例显示将代理项对作为输入的 WriteSurrogateCharEntity 方法。  C#复制  // The following line writes 𐀀. WriteSurrogateCharEntity ('\uDC00', '\uD800'); 下面的示例生成一个代理项对文件，将其加载到 XmlReader 中，并用新的文件名保存文件。 然后，原始文件和新文件被加载回应用程序的 XML 文档对象模型 (DOM) 结构中以进行比较。  C#复制  char lowChar, highChar; char [] charArray = new char[10]; FileStream targetFile = new FileStream("SurrogatePair.xml",       FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);  lowChar = Convert.ToChar(0xDC00); highChar = Convert.ToChar(0xD800); XmlTextWriter tw = new XmlTextWriter(targetFile, null); tw.Formatting = Formatting.Indented; tw.WriteStartElement("root"); tw.WriteStartAttribute("test", null); tw.WriteSurrogateCharEntity(lowChar, highChar); lowChar = Convert.ToChar(0xDC01); highChar = Convert.ToChar(0xD801); tw.WriteSurrogateCharEntity(lowChar, highChar); lowChar = Convert.ToChar(0xDFFF); highChar = Convert.ToChar(0xDBFF); tw.WriteSurrogateCharEntity(lowChar, highChar);  // Add 10 random surrogate pairs. // As Unicode, the high bytes are in lower // memory; for example, word 6A21 as 21 6A. // The high or low is in the logical sense. Random random = new Random(); for (int i = 0; i < 10; ++i) {       lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));       highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));       charArray[i] = highChar;       charArray[++i] = lowChar; } tw.WriteChars(charArray, 0, charArray.Length);  for (int i = 0; i < 10; ++i) {       lowChar = Convert.ToChar(random.Next(0xDC00, 0xE000));       highChar = Convert.ToChar(random.Next(0xD800, 0xDC00));       tw.WriteSurrogateCharEntity(lowChar, highChar); }  tw.WriteEndAttribute(); tw.WriteEndElement(); tw.Flush(); tw.Close();  XmlTextReader r = new XmlTextReader("SurrogatePair.xml");  r.Read(); r.MoveToFirstAttribute(); targetFile = new FileStream("SurrogatePairFromReader.xml",        FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);  tw = new XmlTextWriter(targetFile, null); tw.Formatting = Formatting.Indented; tw.WriteStartElement("root"); tw.WriteStartAttribute("test", null); tw.WriteString(r.Value); tw.WriteEndAttribute(); tw.WriteEndElement(); tw.Flush(); tw.Close();  // Load both result files into the DOM and compare. XmlDocument doc1 = new XmlDocument(); XmlDocument doc2 = new XmlDocument(); doc1.Load("SurrogatePair.xml"); doc2.Load("SurrogatePairFromReader.xml"); if (doc1.InnerXml != doc2.InnerXml) {       Console.WriteLine("Surrogate Pair test case failed"); } 在使用 WriteChars 方法（一次写出一个缓冲区的数据）写出时，输入中的代理项对可能 会在一个缓冲区内被意外拆分。由于代理项值是定义完善的，如果 WriteChars 遇到来自 较低范围或者较高范围的 Unicode 值，它将该值标识为代理项对的一半。当遇到  WriteChars 将导致从拆分代理项对的缓冲区写入的情况时，将引发异常。使用   IsHighSurrogate 方法检查缓冲区是否以高代理项字符结束。如果缓冲区中的最后一个   字符不是高代理项，可以将该缓冲区传递给 WriteChars 方法。  请参见 概念 使用 XmlTextWriter 创建格式正确的 XML XmlTextWriter 的 XML 输出格式设置 XmlTextWriter 的命名空间功能  #endif

user: user-4
content: C标准不太熟悉 对win32来说 可以认为WCHAR就是指utf16  印象中 实际上它定义的是“至少”两字节的字符 但实际用的时候 涉及到API什么的都是用的utf16  utf32 utf16 utf8也已经都有单独的类型 char8_t char16_t char32_t 你如果怕弄混乱可以明确指定这些类型 但使用API的时候免不了经常跟WCHAR也就是实际上存放utf16的字符串转换来转换去 其实还不如直接都用L""好了

user: user-5
content: 宽字符多宽是系统依赖的，在windows上是UTF-16编码，在Linux/Unix上是UTF-32编码，你输出一下sizeof(wchar_t)就知道了