字符编码的问题,上大学那会儿就遇到过,一直都是云里雾里,没太搞清楚。最近又遇到了问题,想在C++的控制台上输出Utf-8编码的汉字字节流。尝试了好多次都是乱码,后来花了些时间查查资料,又和同事交流了一下,算是把C++上对于UTF8编码的处理大概摸清楚了。


字符集


  先说一个名词:字符集,没听过的先百度一下,其实就是一种将字符编码的格式,像我们常说的ASCII,UTF8,GBK都是常用的字符集。

  首先要清楚,从你在编辑器里输入一个UTF8汉字开始,到最终在控制台上显示出来,整个流程涉及到三个概念,分别是源码字符集,执行字符集,解析字符集。

  分别解释一下:

  源码字符集:就是你的源代码文本文件的字符集,如果你手头有NodePad++这样类似的文本编辑器你可以打开看一下你的字符集,或者用Windows记事本另存为的时候也会显示文本格式。要知道,你的源代码文本文件是以二进制的形式躺在硬盘里的,无论中文英文都一样,当你输入一个汉字后保存关闭,这个汉字就是按照你指定的字符集转换成二进制编码保存下去的,当你在以这个格式打开文件时候,就再按照你指定的字符集把二进制转回来。如果两次使用不同的字符集,也就会出现乱码了。

  执行字符集:在C++里 char* str= “我”;执行字符集决定了这行代码在编译器进行编译的时候str存储的字节到底是什么,你可能会说源码字符集不是已经决定了这个”我”的二进制表示了么,没错,但是这个执行字符集就是让你在这里对它再解释一次。比如我源码字符集可能是UTF8的,但是我可以通过执行字符集来让最终ptr存储的是GBK的字节编码。

  解析字符集:最终要还原显示这些二进制字节编码的时候,就需要用到它。比如通过printf把前面的str显示到控制台时,这个printf就会按照解析字符集来解析这些字节编码,找到指定字符显示出来。

  饶了一圈,好像也不是很乱,但是这里面是有很多坑的。这几个字符集的处理都是跟具体编译器甚至操作系统相关的,不同的编译器是有差别的,我这里只说Windows7系统下VS2013(msvc编译器)的环境。


VS2013中的字符集概念


1.对于源码字符集在VS2013文件->高级保存选项->编码中可以查看设置当前源代码文件的源码字符集。

2.对于执行字符集,VS2013默认根据系统的Locale来决定执行字符集,一般大家都是windows中文系统,Locale是中国,那么就是GBK编码。

3.对于解析字符集,我试了一下,如果没有手动更改的话VS2013的标准输入输出(printf)到命令行也是根据系统Locael决定的,也就是GBK。


案例分析


  现在我们就分析一下,假如下面这段源码我们用UTF8格式保存(无Bom).分析一下控制台上显示的结果。

 char* str= “我”;
printf(“%s\n”,str);

1.首先这个代码文件的文本中”我”这个汉字是以E68891三个字节编码的.

2.当编译器编译这段代码时,执行字符集默认是GBK,那么编译器要决定str的字节内容,就要把文本里保存的字节内容转为GBK,这里就有个值得注意的问题,既然要转换到GBK,就需要知道从什么格式转换到GBK,MSVC怎么知道源格式呢?方法只有一个就是分析你的源文件有没有有BOM,要是有就按照BOM它就认为原格式就是BOM指定的格式(不了解BOM可以先百度一下),如果没有BOM他就认为你的源码字符集是Locale关联的。刚才说了我们是用UTF8无BOM格式保存的源文件,所以编译器认为源码文本中的”我”是GBK编码保存的。

3.那从GBK到GBK,MSVC不会进行任何转换,这里有个小问题,提醒一下,这个代码应该是编译不通过的,因为GBK中汉字是2个字节表示的,而UTF8中是三个字节,所以编译器为了凑数会把”我”字后面的双引号给吃掉,转成了两个GBK汉字编码E688,9122(22是引号的UTF8编码),没有引号编译器就会报错,最简单的解决办法就是在在后面在加一个汉字变成偶数个就没问题了。

4.程序运行起来后printf输出到控制台,这时候用到的解析字符集也是GBK的,就会用内存里的E688,9122去GBK字符集里找到对应编码的汉字“鎴?”。这当然就错了。

  字符编码可以到这个网站去查询http://www.mytju.com/classcode/tools/encode_utf8.asp


解决方案


  这就是我一开始出现的错误,既然知道问题了,那怎么改呢,为了让UTF8编码的源文件中的”我”字可以显示到命令行上,我们需要进行如下分析:

1.首先一定要在编译的时候让str的字节内容是UTF8格式的才行,那就需要让执行字符集是UTF8才行,前面说到MSVC执行字符集是根据Locale来决定的,本来是没法更改的,但是微软后来打了个小不定添加了一个预处理#pragma execution_character_set("utf-8")。来告诉编译器执行字符集设置为UTF8。

2.编译时候进行转换到执行字符集需要知道源码字符集,之前我们是没有带BOM,这导致MSVC认为我们的源文件是GBK编码的,但其实我们是UTF8编码,这就需要我们保存源码的时候改为用UTF8带BOM的格式。这样就不会有问题了。

3.最后要显示出来,既然内存里是UTF8编码,解析肯定也要按UTF8格式来解析,所以我们要把默认的解析字符集从GBK设为UTF8,最简单的方法就是在输出之前调用system(“chcp 65001”);这是命令行设置当前代码页的命令。

  这样应该就能正常显示UTF8字符了,不过有个问题就是如果str用cout输出的话,依然是乱码,这个可能是因为cout有自己的解析字符集,不会随着chcp命令改变。这个有待研究,哪位同学知道,可以留言告诉我。再说一点#pragma execution_character_set("utf-8")这个预处理在C++11里已经不再需要了,C++11可以指定字符串字面量的执行字符集了,u8”我”。就这么简单。但是vs2013并不支持这个功能。这篇文章讲述的内容,并不在于如何把一个UTF8格式的C++字面量输出到控制台。而是在于通过这个例子来了解MSVC C++是如何处理UTF8中文字符的。

  尊重他人智慧成果,若要转载,请注明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/MSVC_UTF8_CHARSET_HANDLE.html

MSVC中C++ UTF8中文编码处理探究的更多相关文章

  1. 在msvc中使用Boost.Spirit.X3

    Preface “Examples of designs that meet most of the criteria for "goodness" (easy to unders ...

  2. Pycharm中不支持中文编码的解决方案。Pycharm中文报错。 Pycharm出现的部分快捷键无效及解决办法

    Pycharm中不支持中文编码的解决方案.Pycharm中文报错. 1. 打开Pycharm ---->  File ----> Default setting ------> Ed ...

  3. PHP+MySQL中对UTF-8,UTF8(utf8),set names gbk 的理解

    问题一:在我们进行数据库操作时会发现,数据库中表的编码用的是utf-8,但是在进行dos命令是要使用set names gbk (一)Mysql中默认字符集设置有四级:服务器级,数据库级,表级,和字段 ...

  4. 记住,永远不要在MySQL中使用“utf8”编码[转载]

    记住,永远不要在MySQL中使用“utf8”编码 原创: 无明.Adam 聊聊架构 6月15日 最近工作中我遇到了一个 bug,我试着通过 Rails 在以“utf8”编码的 MariaDB 中保存一 ...

  5. 为 Apache 配置 UTF-8 中文编码

    为 Apache 配置 UTF-8 中文编码 cat /etc/httpd/conf/httpd.conf | grep -n utf -C2 30-# 31-ServerRoot "/et ...

  6. SQL学习笔记之MySQL中真假“utf8” 问题

    0x00 MySQL中UTF8报错 最近我遇到了一个 bug,我试着通过 Rails 在以“utf8”编码的 MariaDB 中保存一个 UTF-8 字符串,然后出现了一个离奇的错误: Incorre ...

  7. 不要在 MySQL 中使用“utf8”,请使用“utf8mb4”

    不要在 MySQL 中使用“utf8”,请使用“utf8mb4” 最近我遇到了一个bug,我试着通过Rails在以“utf8”编码的MariaDB中保存一个UTF-8字符串,然后出现了一个离奇的错误: ...

  8. 谨记不要在MySQL中使用“utf8”编码

    掉坑回顾: 最近在工作中遇到一个BUG,用于记录客户昵称的数据表,在插入带有表情的字符时候报错.使用的存储引擎是INNODB,当我查看数据库字段的时候确实是设置的utf8,我传入的字符也是utf8的编 ...

  9. 为什么不要在MySQL中使用UTF-8编码方式

    MySQL的UTF-8编码方式 MySQL 从 4.1 版本开始支持 UTF-8,也就是 2003 年,然而目前流行的UTF-8 标准(RFC 3629)是在此之后规定的.正因此,才造就了MySQL中 ...

随机推荐

  1. Could not find a version that satisfies the requirement PIL

    Python Imageing Library 简称 PIL Python常用的图像处理库之一 Pillow是PIL一个fork. C:\Users\dangzhengtao>pip insta ...

  2. Centos7编译opencv3.4.1

    Centos7编译opencv3.4.1 参考博客 https://blog.csdn.net/wjbwjbwjbwjb/article/details/79111996 1.配置epel源 yum ...

  3. reset()方法的使用、jq下面reset()的正确使用方法

    reset()是 原生js的的方法,所有浏览器都支持,而且必须是form元素包括下的表单元素,但是JQuery中没有reset方法, 效果图:  错误用法: 正确用法: js用法: document. ...

  4. 1040 mysql Too many connections

    笔者在项目中遇到mysql 出现:1040 too many connections 异常,意思是超过数据库最大连接数,打不开表结构信息.笔者排除问题建议:1.查看程序代码是否存在BUG:2.检查代码 ...

  5. ubuntu18.04 配置chrome ss

    为了FQ 不FQ不行啊 安装shaowsocks 1. 用实验室的服务器 sslocal -c xxx.json 2. 下载SwitchyOmega插件 链接: https://pan.baidu.c ...

  6. 学习笔记DL006:特征分解,奇异值分解

    特征分解. 整数分解质因素. 特征分解(eigendecomposition),使用最广,矩阵分解一组特征向量.特征值.方阵

  7. kali在执行 apt-get update 命令时报错的解决方法

    报错内容: root@kali:~# apt-get updateGet:1 http://kali.mirror.garr.it/mirrors/kali kali-rolling InReleas ...

  8. svn的分支与合并

    作者:fbysss msn:jameslastchina@hotmail.com  blog:blog.csdn.net/fbysss 声明:本文由fbysss原创,转载请注明出处 关键字:svn分支 ...

  9. Ubuntu 终端使用ss代理

    用polipo软件,这个软件可以吧socket5转换成http代理 $ sudo apt-get install polipo $ sudo vim /etc/polipo/config 在文件中加入 ...

  10. MariaDB的安装与启动

    MariaDB的安装与启动 1.安装前需要删除系统已存在的mysql及mariadb [root@vm172--- ~]# rpm -qa|grep mysql [root@vm172--- ~]# ...