您的位置:首页 > 其它

WideCharToMultiByte 和 MultiByteToWideChar 之中文打印问题

2017-08-05 19:17 447 查看
该文是编码问题的第四篇。前三篇如下。

文本文件与字符编码

WideCharToMultiByte 和 MultiByteToWideChar 之 codePage 参数(一)

WideCharToMultiByte 和 MultiByteToWideChar 之 codePage 参数(二)

通过前面三篇,基本可以搞懂字符内码,编码,转化问题。该篇研究的是中文打印问题。

以下代码尝试在作变换的同时,用 printf 将中文打印出来。

void testEncode2()
{
wchar_t* str = L"我是李志浩";
printf("%s\r\n", str);//oops, can't work
printf("%ls\r\n", str);//oops, can't work, even use '%ls'
wprintf(L"%ls\r\n", str);//oops, can't work, even use wprintf and '%ls'

size_t charLen = 0;
char * chars = wideCharSetToDefaultMult(str,tstrLen(str),&charLen);
printf("%s\r\n", chars);		//ok, work

size_t wcharLen = 0;
wchar_t * wchars = defaultMultCharSetToWide(chars,charLen,&wcharLen);
assert(strEqual(str,wchars));

size_t glen;
char *gchars = wideCharSetToMult(str,tstrLen(str),936,&glen);
printf("g: %s\r\n", gchars);	//oops, can't work

assert(strEqual(chars,gchars));	//to prove the system code page is 936

size_t utf8CharsLen;
char *utf8Chars = wideCharSetToUtf8Mult(str,tstrLen(str),&utf8CharsLen);
printf("%s\r\n", utf8Chars);	//oops, can't work

char *cstr = "cc 我是李志浩";
printf("%s\r\n", cstr);			//ok, work
system("PAUSE");
}


上面标出了哪些可以正确打印,哪些不能。结合上面的结果,作以下分析。

1.printf 或 wprintf 都不能打印中文

2.printf 可以直接打印由中文的宽字节字符串转化得到的系统默认编码的多字节字符串

3.连续使用 WideCharToMultiByte 和 MultiByteToWideChar 转化,输出与原始输入是一致的

4.系统默认的 code page 是 936

后面两点在前一文章中已经提到过。

printf 和 wprintf 是 C 库函数,思考,将一个字符串给这两个函数时,它们是如何将其打印出来的呢?至少 printf 不会知道如何去绘制字符。实际上,这一过程必然涉及到,将字符串解码并显示。

一台部署在爪洼国的电脑,应该不会知道如何去显示中文字符!前面说了,操作系统实际上处理的字符是宽字符,因为这是全球通用的标准。所以要显示的字符串,必然要先转化为这个通用的标准。我们传入的中文字符串,必然不被操作系统直接理解,而我们需要做的,是给 printf/ wprintf 一个标识:这个字符串的编码是什么,它才能将转化好的标准内码送给操作系统,或者是将编码和源串一起给操作系统(估计不是这种方式),之后由操作系统负责调用驱动程序打印。

配合 printf 或 wprintf 做这件事的就是: setlocale(LC_ALL, "chs");

它告诉 C 语言库,当前运行的语言环境是什么,应该把当前的多字节序列以怎样的格式去解析。

下面是在打印前插入这句话后的效果。

void testEncode2()
{
setlocale(LC_ALL, "chs");

wchar_t* str = L"我是李志浩";
printf("%s\r\n", str);			//oops, can't work. because 'printf(%s)' looks str as char sequence
printf("%ls\r\n", str);			//ok
wprintf(L"%ls\r\n", str);		//ok

size_t charLen = 0;
char * chars = wideCharSetToDefaultMult(str,tstrLen(str),&charLen);
printf("%s\r\n", chars);		//ok, work

size_t wcharLen = 0;
wchar_t * wchars = defaultMultCharSetToWide(chars,charLen,&wcharLen);
assert(strEqual(str,wchars));

size_t glen;
char *gchars = wideCharSetToMult(str,tstrLen(str),936,&glen);
printf("g: %s\r\n", gchars);	//ok

assert(strEqual(chars,gchars));

size_t utf8CharsLen;
char *utf8Chars = wideCharSetToUtf8Mult(str,tstrLen(str),&utf8CharsLen);
printf("%s\r\n", utf8Chars);	//oops, can't work, because printf(%s) looks utf8Chars as char sequence with gb2312 encoded,
//which will cause error decode

char *cstr = "cc 我是李志浩";
printf("%s\r\n", cstr);			//ok, work
system("PAUSE");
}


还有两个打印失败,后面也给出了原因。

第一个是因为 printf 函数在使用 %s 时将要打印的参数看作 char 序列,这显然是不对的。

第二个是因为 printf 函数在使用 %s 时将传入的 char 序列当成了 gb2312 编码.(这是由于前面setlocale 的作用),而实际上入参序列是 utf8 编码的,解析时必然出错导致无法打印。

最后需要补充的是,printf 和 wprintf 的意义并不是前者用来打印多字节序列,后者用来打印宽字节序列,它们只是的分别只在于,前者接收的 format 串,一个是多字节序列的,一个是宽字节序列的:

printf("",...)

wprintf(L""...)

如第二个打印,printf 也可以用来打印宽字节序列,使用 %ls 即可。此时 printf(%ls) 将入参看作宽字节序列。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: