Linux C语言环境下如何输出宽字符 -- 转载

简介: 资源来自http://bbs.chinaunix.net/viewthread.php?tid=1083940 自己整理如下 C++#include #include #include...

资源来自http://bbs.chinaunix.net/viewthread.php?tid=1083940

 

自己整理如下

 

C++
#include <iostream>
#include <string>
#include <locale>

using namespace std;

int main(void) {
  wstring w = L"Out中文";

  ios_base::sync_with_stdio(false);
  wcin.imbue(locale("zh_CN.utf8"));
  wcout.imbue(locale("zh_CN.utf8"));
  wcout << w << endl;
}
 


C
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>

int main(void) {
  wchar_t w[] = L"Out中文";
  setlocale(LC_ALL, "zh_CN.UTF-8");
  wprintf(L"%ls/n", w);

  return 0;
}

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cnhome/archive/2008/11/19/3335193.aspx

 

 

今天在CSDN的Blog首页看到一篇文章“也谈计算机字符编码 ”,由于前一阵业余翻译了“UTF-8 and Unicode FAQ for Unix/Linux”一文,自己对字符集、编码和Unicode等内容一直保着者很强的兴趣,自然不会放过这样的文章。

作者的文章写得很明白易懂,虽然有一些概念上的细节问题我觉得有商榷之处;作者还给出一个简单的在windows下使用wprintf正确输出字符串“中文”的小例子,我linux下模仿作者给出的示例代码写了如下的示例代码:

#include <cstdio>
#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"

然而程序的运行结果却让我很诧异

whodare@whodare:$ ./a.out
-N
我的第一反应就是作者的示例代码是不是有问题,毕竟这里面调用的全都是C的标准库函数,不应该存在移植性问题;然而,我找了台windows机器测试作者的代码,结果让我很郁闷,一切正常......

为 什么我在Linux下的程序就不对呢?我很不服气,于是开始以各种关键字进行搜索,想看看别人是否遇到过类似的问题。一个搜索结果引起了我的主意,有人说 问题出在wprintf中的格式转换符上,将%s替换成%ls就没有这样的问题。带着几分怀疑,我修改了上面的程序,编译运行后,居然真的就没问题了

#include <cstdio>
#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;
}
上述代码的运行结果
whodare@whodare:$ ./a.out
-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 [ ]来存放“中文”时有什么不同
    char    str[]="中文";
    wchar_t wstr[] = L"中文";   
我们使用gdb这个强大的工具来查看str[]和wst[]中究竟都存放了哪些值(请注意颜色之间的对应关系)

(gdb) x /8xb &str
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

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

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 (最初的代码!)
wprintf(L"%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

wprintf(L"%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,单独用于表示对应参数是宽字符串

       谁能告诉我该问题的答案,不盛感激.......


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lovekatherine/archive/2007/11/06/1868724.aspx

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
3天前
|
关系型数据库 MySQL Linux
在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾
以上就是在Linux环境下备份Docker中的MySQL数据并传输到其他服务器以实现数据级别的容灾的步骤。这个过程就像是一场接力赛,数据从MySQL数据库中接力棒一样传递到备份文件,再从备份文件传递到其他服务器,最后再传递回MySQL数据库。这样,即使在灾难发生时,我们也可以快速恢复数据,保证业务的正常运行。
61 28
|
14天前
|
Ubuntu Linux PHP
利用PHP压缩音频:Linux环境下的ffmpeg简易安装指南
希望这个指南能为你的编程之旅提供帮助。只需记住,每一行代码都像音乐的音符,组合在一起,创造出美妙的旋律。祝你编程愉快!
58 6
|
1月前
|
关系型数据库 MySQL 应用服务中间件
Linux 手动安装快速部署 LNMP 环境实战
本文详细记录了在阿里云ECS上手动搭建LNMP环境的过程,系统选用Ubuntu 24.04。主要内容包括:1) 使用`apt`安装Nginx和MySQL,并更新软件源;2) 编译安装PHP 8.4.5,配置PHP-FPM及环境路径;3) 配置MySQL root用户密码;4) 调整Nginx支持PHP解析并测试整体环境。通过此过程,重现手动配置服务器的细节,帮助熟悉各组件的安装与协同工作。
102 23
|
2月前
|
关系型数据库 应用服务中间件 Linux
Linux云服务器如何搭建LNMP环境
LNMP环境是Linux系统中常用的Web服务架构,由Linux、Nginx、MySQL/MariaDB和PHP组成,适用于高效托管动态网站。本文以CentOS 7为例,详细介绍了LNMP环境的搭建步骤,包括Nginx、MariaDB和PHP的安装与配置,以及最终通过创建`index.php`文件验证环境是否成功部署。具体操作涵盖配置YUM仓库、安装服务、编辑配置文件、启动服务等关键步骤,确保用户能够顺利搭建并运行LNMP环境。
67 1
Linux云服务器如何搭建LNMP环境
|
4月前
|
Ubuntu Linux Shell
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
(已成功解决)Linux环境报错—bash: wget: command not found;常见Linux发行版本,Linux中yum、rpm、apt-get、wget的区别;Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
1486 68
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
|
6月前
|
安全 Linux 网络安全
Linux环境中安装和使用Paramiko
Linux环境中安装和使用Paramiko
352 12
|
24天前
|
JavaScript Ubuntu Linux
如何在阿里云的linux上搭建Node.js编程环境?
本指南介绍如何在阿里云Linux服务器(Ubuntu/CentOS)上搭建Node.js环境,包含两种安装方式:包管理器快速安装和NVM多版本管理。同时覆盖全局npm工具配置、应用部署示例(如Express服务)、PM2持久化运行、阿里云安全组设置及外部访问验证等步骤,助你完成开发与生产环境的搭建。
|
6月前
|
Java 关系型数据库 MySQL
Linux环境
Linux环境
65 5
|
4月前
|
消息中间件 Java Kafka
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
本文介绍了Kafka集群的搭建过程,涵盖从虚拟机安装到集群测试的详细步骤。首先规划了集群架构,包括三台Kafka Broker节点,并说明了分布式环境下的服务进程配置。接着,通过VMware导入模板机并克隆出三台虚拟机(kafka-broker1、kafka-broker2、kafka-broker3),分别设置IP地址和主机名。随后,依次安装JDK、ZooKeeper和Kafka,并配置相应的环境变量与启动脚本,确保各组件能正常运行。最后,通过编写启停脚本简化集群的操作流程,并对集群进行测试,验证其功能完整性。整个过程强调了自动化脚本的应用,提高了部署效率。
【手把手教你Linux环境下快速搭建Kafka集群】内含脚本分发教程,实现一键部署多个Kafka节点
|
6月前
|
Web App开发 搜索推荐 Unix
Linux系统之MobaXterm远程连接centos的GNOME桌面环境
【10月更文挑战第21天】Linux系统之MobaXterm远程连接centos的GNOME桌面环境
1157 5
Linux系统之MobaXterm远程连接centos的GNOME桌面环境