https://www.zhihu.com/question/28164512java
关于编码和乱码的问题,我简单讲一下。python
一般问这类问题的人是混淆了若干个不一样的概念,而且他们本身也没有意识到本身混淆了这些概念的。linux
- 终端显示字符的编码(windows下终端是cmd,linux下是各类terminal,远程登陆是putty或者xshell)
- shell环境的编码。好比中文版windows用的是gbk(向下兼容gb2312),大多数linux发行版使用的是utf-8(LANG=zh_CN.UTF-8)。
- 文本文件的编码。这个一般取决于你的编辑器,并且有的编辑器支持多种编码的话,你能够在文本开头位置指定编辑器使用特定编码。好比# -*- coding: utf8 -*-,vim看到这行会默认将这个脚本认定为utf-8兼容编码格式。
- 应用程序的内部编码。一个字符串,做为数据只是一个字节数组,可是做为字符的数组,就有一个解析方式。java和python的内部字符编码是utf-16,python和java都支持用不一样的编码来对字节数组进行decode来获得字符数组。
拿题主的问题来解释一下。shell
我在ubuntu kylin中文环境下默认terminal中作了一样的实验,可是结果和题主刚好相反:ubuntu


看见没有?vim
题主和我都没有说谎,这是为何呢?
由于windows
这坨代码的含义其实是:将这里显示的这坨看上去像“汉字”的东西,用gb2312解码,转换为unicode字符串。unicode("汉字","utf-8")相似,只不过是用utf-8解码,转成unicode字符串。
(注:这里涉及到两个概念——unicode字符集和utf-8编码——不少时候会用混淆,一个字符集表示一堆符号,而一种编码是用二进制表示这个字符集的一种编码方式。一样是unicode字符集,能够有utf-八、utf-1六、utf-32等等编码方式。)数组
那这里显示的看上去像“汉字”的,tmd的究竟是个什么东西?编辑器
- 若是是在个人环境下,也就是linux utf-8环境下一个utf-8显示终端,能显示成“汉字”的这坨东西,它其实是以utf-8编码的“汉”字和“字”字两个unicode字符。它们的真实字符值就是u'\u6c49\u5b57'(内码),能够用"汉字".encode("hex")来查看当前终端下(utf-8编码值)的十六进制码。
- 。因此个人命令是,将'e6b189e5ad97'这坨字节数组,转换为unicode的字符数组。——结果毫无难度,没有错误,由于它原本就是utf-8编码,因此可以正常做为unicode字符解码。
可是unicode("汉字", "gb2312")就不同了,这个命令等同于“将'e6b189e5ad97'这坨东西,用gb2312编码方式来解码成字符”,可是实际上因为编码空间并不兼容,使用gb2312编码方式没法解码这么一坨奇葩的数据,因此葛屁了。
- 在题主的环境下,由于系统终端和默认文件编码都是GBK,因此这个数其实是

这个其实是gbk(兼容gb2312)的字符“汉字”的真实字节数组。
因此对这坨数据作unicode("汉字","utf8")会失败——由于无论你怎么想,虽然看上去是同样,可是实际上不是同一坨东西啊!
- 题主如今弄了一个文件,在开始加上了
# -*- coding: utf8 -*-
这下编辑器看到了,知道这文件是utf-8的了。因此编辑器对读入的一坨坨字节用utf-8来解码,对于输出到磁盘的汉字也用utf-8来编码。因此你在文件里面看到的看上去像“汉字”的东西,就和第一种状况下想同了,固然代码就跑得通。
顺便说一下,若是编辑器无视行首这行编码声明,或者编辑器没法支持utf-8格式,那么你弄好的文件在那个编辑器下就会显示乱码,多么简单的道理啊。
因此,要可以正常的显示中文(或者其余什么乱七八糟奇葩的多字节文字),如下条件缺一不可:
- 终端和环境的编码一致(本机一般是一致的,不一致经常出如今远程登陆);若是不一致就须要有编辑器或者文本阅读器作一个兼容二者的转换。
- 编辑器可以认识文本编码
- 系统拥有能显示这种字符的字体。
这也就是我为何一直反对在程序文本中使用除ascii以外的全部编码字符的缘由。环境太复杂了,绕开问题远比解决问题轻松。字体