浅谈C中的wprintf和宽字符显示
今天在CSDN的Blog首页看到一篇文章“ 也谈计算机字符编码 ”,由于前一阵业余翻译了 “UTF-8 and Unicode FAQ for Unix/Linux”一文,自己对字符集、编码和Unicode等内容一直保着者很强的兴趣,自然不会放过这样的文章。
作者的文章写得很明白易懂,虽然有一些概念上的细节问题我觉得有商榷之处;作者还给出一个简单的在windows下使用wprintf正确输出字符串“中文”的小例子,我linux下模仿作者给出的示例代码写了如下的示例代码:
#include < cstdlib >
#include < clocale >
#include < cwchar >
int main( int argc, char * argv[])
... {
wchar_t wstr[] = L"中文";
setlocale(LC_ALL, "zh_CN.UTF-8");
wprintf(L"%s/n",wstr);
return 0;
}
这里需要说明的是我的机器的locale为"zh_CN-UTF-8"
然而程序的运行结果却让我很诧异
-N
我的第一反应就是作者的示例代码是不是有问题,毕竟这里面调用的全都是C的标准库函数,不应该存在移植性问题;然而,我找了台windows机器测试作者的代码,结果让我很郁闷,一切正常......
为 什么我在Linux下的程序就不对呢?我很不服气,于是开始以各种关键字进行搜索,想看看别人是否遇到过类似的问题。一个搜索结果引起了我的主意,有人说 问题出在wprintf中的格式转换符上,将%s替换成%ls就没有这样的问题。带着几分怀疑,我修改了上面的程序,编译运行后,居然真的就没问题了
#include < cstdlib >
#include < clocale >
#include < cwchar >
int main( int argc, char * argv[])
... ... {
wchar_t wstr[] = L"中文";
setlocale(LC_ALL, "zh_CN.UTF-8");
wprintf(L"%s ",wstr);
wprintf(L"%ls ",wstr);
return 0;
}
上述代码的运行结果
- N
中文
问题解决了,可我还是感到迷茫:格式转换符"ls"和“s"的区别是什么?为什么原来的程序会出问题?“-N"这个字符串是怎么冒出来的?为什么作者在windows下的程序就不存在该问题?
这么多的疑惑堵在心口,我哪能心安呢。知其然还要知其所以然嘛!花了一个下午的时间仔细读了下wprintf的manual,并在gdb的帮助下做了各种试验,终于算是把我的疑惑基本都解决了。
一、以下的所有试验都是以“中文”为例,因此有必要先把它的Unicdoe码值、UTF-8编码都列出来,以便于更好的理解下文
‘中’ Unicode码值:U+4E2D UTF-8 编码 e4 b8 ad
‘文’ Unicode码值:U+6587 UTF-8 编码 e6 96 87
二、我们需要理解用char[ ]和wchar_t [ ]来存放“中文”时有什么不同
wchar_t wstr[] = L " 中文 " ;
我们使用gdb这个强大的工具来查看str[]和wst[]中究竟都存放了哪些值(请注意颜色之间的对应关系)
0xbf83decd : 0xe4 0xb8 0xad 0xe6 0x96 0x87 0x00 0xf0
(gdb) x / 12xb & wstr
0xbf83dec0: 0x2d 0x4e 0x00 0x00 0x87 0x65 0x00 0x00
0xbf83dec8: 0x00 0x00 0x00 0x00
不难看出,char str[ ]中存储的是“中文"的UTF-8编码,这是因为我的机器的locale是zh_CN.UTF-8,程序源文件的自然采用的是UTF-8编码,因此编译器 在处理 char str[ ]="中文"; 时,t它对str[]所做得初始化实际上可以理解成 char str[ ]={ 0xe4,0xb8,0xad,0xe6,0x96,0x87,0x00}
而wchar_t wstr[ ]中存放的是“中文"的Unicode码值,这符合C标准对宽字符的定义。这里需要解释的是C标准中规定宽字符是16 bit的字符,而从GNU glibc 2.2开始,类型wchar_t只用于存放32-bit的ISO 10646码值(你可以粗略的把ISO 10646理解成Unicode,尽管它们并不是一回事),而独立于当前使用的locale;因此在上面的输出中,我们看到每个Unicode码值用 32bit表示,而不是16bit。
三、关于%s和%ls的区别
我搜到了一篇帖子(很伤感,我再此发现在CS领域,最靠的住的资料总是英文的),里面对各种格式转换符有详细的解释,愿意看原文的同学直接忽略本段文字.......
http://www-ccs.ucsd.edu/c/lib_prin.html
首先,%ls和%s的区别很简单,%ls意味着将对应的参数会被当作基于宽字符的字符串(wide chraracter string )看待,而%s则意味着对应的参数会被当作普通字符串(multi-byte string)看待。
其次,不要因为上面一句话而错误的认为%s只用于printf,而%ls只用于wprintf 。实际上,(printf, wprintf) 和(%s,%ls)这两个元组之间是相互独立的,也就是说它们之间的四种组合都是可以的。
再次,printf用于byte stream,即输出流中的每个字符颤1 byte;而wprintf则用于wide stream,输出流中的每个字符不止 1 byte。
说了一堆废话,还是结合实例来看看%ls和%s的区别吧
例子1 printf + %s + wstr
whodare@whodare:$ . / a. out
- N
哈,这个郁闷的"-N"又一次出现!为什么会出现呢?让我来分析一下printf在执行时所完成的操作吧。
这里用了%s, printf 就会将对应的参数wstr视为普通字符串(尽管我们清楚他是个wcs而不是mbs);另一方面,我们已经看到了wstr[ ]的内存布局,其前3 byte为 0x2d ,0x4e,0x00。我们都知道C中的字符串以'/0'为结束标志,因此printf只会处理wstr[ ]中的前三个byte,而查一查ASCII表,0x2d对应字符'-',0x4e对应字符'N',所以我们会看到”-N"这个诡异的输出。
例子2 printf + %ls + wstr
whodare@whodare:$ . / a. out
中文
使用了%ls,printf会将对应的参数视为宽字符串(wcs),而printf又对应byte stream,因此这里要对宽字符(wcs)进行转换,变成普通的字符串(mbs)。这里的转换是printf通过对每个宽字符隐式的调用wcrtomb ()这个标准库函数完成的。按么,wcrtomb()这个函数进行是按照什么规则进行转换的?这就是setlocale()的作用所在了,wcrtomb 会依据程序员设定的locale,将wcha_t中存放的码值,转换为相应的的多字节编码。
回到例子中,我的机器的locale为zh_CN.UTF-8,对应的编码为UTF-8,因此wstr[ ]中存放的Unicode码值会转换为UTF-8编码的形式输出到标准输出流中,这样采用UTF-8编码的console就能正确识别受到的字节流并显示出"中文"
例子3 wprintf + %s +wstr (最初的代码!)
whodare@whodare:$ . / a. out
- N
使用了%s,wprintf会将对应的参数视为普通字符串mbs,尽管我们还是很清楚它其实是个wcs。wprintf 使用的是wide stream,因此需要将所给的mbs参数转换为wcs再由wprintf完成输出;这个转换是由wprintf隐式的对mbs不断调用mbrtowc来 完成,转换规则依然是和locale相关的。
我们知道wstr的内存布局为:
0x2d 0x4e 0x00 0x00 0x87 0x65 0x00 0x00
0x00 0x00 0x00 0x00
该"mbs"的转换结果为 L‘0x2d' + L '0x4e' + L '0x00' ,最终输出结果又是讨厌的"-N"
例子4 wprintf + %ls+ wstr
whodare@whodare:$ . / a. out
中文
使用了%ls,wprintf会将对应参数视为宽字符串wcs,这次终于没有搞错。因此wprintf会顺利的将给定的宽字符串写入标准输出流,最终正确显示"中文"
看完这4个例子,你对wprintf、printf和%ls 、%s的使用还有疑惑么?
四、小结
1。要清楚%ls和%s的意义在于指明所期待的参数是何种字符串,而printf和wprintf的区别在于所使用的是不同类型的stream
2。貌似在linux下输出“中文"的正确方法是 wprintf( "%ls/n",L"中文") ,而引文中作者在Windows成功操作的wprintf("%s/n", L"中文")在linux无法正确工作,至于为何wprintf这个标准库函数在两个系统下有不同表现,我是无心再向下深挖了,难道这又是VC一处不符合 标准的地方?.......
3 。貌似还有一个%S,单独用于表示对应参数是宽字符串
谁能告诉我该问题的答案,不盛感激.......
浅谈C中的wprintf和宽字符显示相关推荐
- php css定位到图片上,CSS_浅谈css中图片定位之所有图标放在一张图上,如今做网页为了使网站丰富多 - phpStudy...
浅谈css中图片定位之所有图标放在一张图上 如今做网页为了使网站丰富多彩,富于表现力,往往需要应用大量的图片/图标.如何处理这些图片,使其尽量不影响网页载入,解析等速度,是一个不大不小的问题.如果你的 ...
- java 中的单元测试_浅谈Java 中的单元测试
单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...
- mybatis与php,浅谈mybatis中的#和$的区别
浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...
- 浅谈CSS3中display属性的Flex布局,关于登陆页面属性框的设置
声明:本文转发自三里屯柯南的浅谈CSS3中display属性的Flex布局http://www.cnblogs.com/xuyuntao/articles/6391728.html 基本概念 采用Fl ...
- python sys模块作用_浅谈Python中的模块
模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Mod ...
- python生成器和迭代器作用_浅谈Python中的生成器和迭代器
迭代器 迭代器协议 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么返回一个异常来终止本次迭代.(只能往前走,不能往后退!) 迭代器对象 遵循了(实现了)迭代器协议的对象.(对象内 ...
- oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念
Nested loop join:
Outer - phpStudy...
浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...
- 浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别
浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别 标签: caffe深度学习CaffeNet 2016-11-02 16:10 1203人阅读 评论(1) ...
- python中 是什么类型_浅谈python中的变量默认是什么类型
浅谈python中的变量默认是什么类型 1.type(变量名),输出的结果就是变量的类型: 例如 >>> type(6) 2.在Python里面变量在声明时,不需要指定变量的类型,变 ...
最新文章
- 网速,果然如此!| 每日趣闻
- django-admin 设计User外键,设计model
- 使用javabean类用户注册
- 附近的人 php,php 附近的人
- HarmonyOS之深入解析媒体会话的管理
- 大四课程设计之基于RFID技术的考勤管理系统(三)数据库设计
- MySQL的两个存储引擎--MyISAM和InnoDB
- 6 SystemVerilog语言编写售货机
- 枚举算法:求两个整数m,n(m>n)最大公约数的欧几里得算法描述。
- spark 尽量避免数据源的数据倾斜
- Illustrator 教程,如何在 Illustrator 中编组内容?
- 捷联惯导系统模型及仿真(二)
- wowza 技术交流群/ wowza 流媒体软件交流群
- windows 匿名管道: 父进程与子进程通信 (进程间通信之CreatePipe)
- matlab:xlsread
- 【C/C++】__stdcall、__cdcel和__fastcall定义与区别
- Unity脚本:寻找血量最低的敌人
- Cadence OrCAD Capture 在图纸中添加系统框图的方法
- python sin_Python入门之三角函数sin()函数实例详解
- 常见CRC算法与C实现