您的位置:首页 > 其它

win、py、notepad++的编码方式及问题

2016-07-09 15:52 260 查看

win、py、notepad++的编码方式及问题

先说结论:

因为win的cmd默认使用的编码方式是gbk(ANSI) 所以遇到bat或者python中的中文需要在cmd中print显示时,如果出现乱码,首先需要检查是否因为不是 1. gbk编码的中文或2. 在代码中被转换为gbk编码或3. 在python代码中是unicode对象

别在win下用自带的文本文档编辑器打开utf-8编码的文件并保存,因为win默认会在utf-8编码的内容中加BOM,只是查看还没问题因为不保存不会自动加BOM,但是如果保存的话就win就会自动的加BOM

注意notepad++的设置,即
设置->首选项->新建
新建文档的默认编码方式
推荐还是按照win默认的ANSI保存,否则创建的utf-8文档若被自带的文档编辑器打开,就会自动加BOM,导致一些未可知的运行错误

对于在win下用notepad++写.py 虽然不会有被打开编辑导致加BOM的问题,但是因为有时会需要在cmd中显示,所以最好还是不要用utf-8编码,用ANSI即gbk编码吧

python代码前面添加的
#!coding=utf-8
或者
#!coding=gbk
等表示编码方式的含义是给python的解释器看的,结论就是最好与文档本身的编码方式相吻合,对于win下面的python代码,也就是最好是
#!coding=gbk


ok,说完了上面的结论,开始说一下具体的细节。

写这个文档的原因是因为在notepad++写python代码时遇到的一个问题:

1 前情提要:

在notepad++中安装了python Script插件使得可以使用notepad++作为一个IDE编辑器。

因为之前写的一些python代码涉及到中文路径,以及一些中文注释,所以在notepad++中直接用了
#!coding=utf-8
,但是.py文件本身的编码方式还是默认的gbk。

之前在notepad++的设置中设置了
使用utf-8应用于打开ANSI文件
,这是个很奇怪的功能,就是说文件本身编码的方式还是gbk,但是notepad++打开后先转码成了utf-8并显示,然后在保存的时候还是按原来的方式,即gbk的编码保存,引起了后面分析一系列的误解,之后再详细说明。

2 问题出现的情形:

本身这种“编码关系错乱”的python代码直接在notepad++中编辑并使用Python Script运行或直接使用python的解释器运行时是没问题的,也就是说—-中文路径可以识别,且能够正确读出路径中的文件等

将这种代码使用pycharm打开,在IDE中显示代码中的中文就变成了乱码,同时,也无法运行了。

3 问题的分析:

3.1 字符的编码方式

* ASCII码*

八个二进制位(bit)可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。

ASCII码一共规定了128个字符的编码,比如空格”SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。

非ASCII编码

英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。 但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0–127表示的符号是一样的,不一样的只是128–255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。 中文编码的问题需要专文讨论,这篇笔记不涉及。这里只指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的

Unicode

正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。 Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字”严”。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。 需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字”严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。 它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。

UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。 UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。 UTF-8的编码规则很简单,只有二条:

1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2. 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

5. Unicode符号范围 | UTF-8编码方式
6. (十六进制) | (二进制)
7. --------------------+---------------------------------------------
8. 0000 0000-0000 007F | 0xxxxxxx
9. 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
10. 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
11. 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字”严”为例,演示如何实现UTF-8编码。 已知”严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此”严”的UTF-8编码需要三个字节,即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,从”严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,”严”的UTF-8编码是”11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

win下的编码与编码格式转换

通过上一节的例子,可以看到”严”的Unicode码是4E25,UTF-8编码是E4B8A5,两者是不一样的。它们之间的转换可以通过win的记事本实现。

打开文件后,点击”文件”菜单中的”另存为”命令,会跳出一个对话框,在最底部有一个”编码”的下拉条。

里面有四个选项:ANSI,Unicode,Unicode big endian 和 UTF-8。

1. ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。

2. Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。(也就是说win下这样转换是little endian,但是从阅读顺序来看这是反的,下面会有说明

3. Unicode big endian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。

4. UTF-8编码,也就是上一节谈到的编码方法。

5. 选择完”编码方式”后,点击”保存”按钮,文件的编码方式就立刻转换好了。

Little endian和Big endian 上一节已经提到,Unicode码可以采用UCS-2格式直接存储。以汉字”严”为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。 因此,第一个字节在前,就是”大头方式”(Big endian),第二个字节在前就是”小头方式”(Little endian)。

那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码? Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。

1. 如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;

2. 如果头两个字节是FF FE,就表示该文件采用小头方式。

试用”我”举例:

**big edidon  FE FF**

我:\u 62 11

o:  \u 00 6F

**small edidon FF FE**

我:\u 11 62

o:  \u 6F 00

**utf-8**

我: E6 88 91

o:   6F

PS:对于win系统,是会出现bom标识符的,也就是EF BB BF

**ANSI(对于win系统,就是默认的gbk或gb2312方式)**
我:CE D2
o:6F


ps:网上有一些这种文字编码转换的网站,但是网上的转换因为浏览器的编码问题 一般显示时候很容易出错,可以以这些测试

3.2 notepad++中字符的编码

既然了解了这些编码方式,如何而我的保存为gbk的
#!coding=utf-8
的文档究竟是什么格式呢,在这里尝试使用查看文件二进制编码的程序进行查看,然后就发现一个很奇怪的现象—-将文件本身拖到二进制查看器,比如beyond compare中时,显示的是gbk编码(因为中文的”我”是CE D2 ,但在notepad++中用hex插件却发现显示的是utf-8编码。一开始以为是notepad++会自动根据文件头上
#!coding=utf-8
调整字符的编码方式,结果真相却是在这个文章最前面所说的
新建文档的默认编码方式
中选择了
使用utf-8打开gbk编码文件


因此,在notepad++中所有编码为gbk的文件打开后在显示是都被转换到utf-8的编码,所以在notepad++中的hex插件显示文件是utf-8的编码以及在notepad++中点击
以ANSI显示
会出现乱码 但是退出的时候还是还原到gbk

解决方案:取消上面所说的这个选项,具体参见本文开头

3.3 python中字符的编码

在本节,以’哈’来解释作示例解释所有的问题,”哈”的各种编码如下:

UNICODE (UTF8-16):       C854;
UTF-8:                              E59388;
GBK:                                 B9FE。


3.3.1 python中的str和unicode

python中的str和unicode到底是一个什么东西呢? 在python中提到unicode,一般指的是unicode对象,例如’哈哈’的unicode对象为
u'\u54c8\u54c8'
而str,是一个字节数组,这个字节数组表示的是对unicode对象编码(可以是utf-8、gbk、cp936、GB2312)后的存储的格式。这里它仅仅是一个字节流,没有其它的含义,如果你想使这个字节流显示的内容有意义,就必须用正确的编码格式,解码显示。 例如:



对于unicode对象哈哈进行编码,编码成一个utf-8编码的str—-s_utf8,s_utf8就是是一个字节数组,存放的就是’\xe5\x93\x88\xe5\x93\x88’,但是这仅仅是一个字节数组,但是如果将utf-8编码的字节数组直接print,就会显示乱码,为什么

因为print语句它的实现是将要输出的内容传送了操作系统,操作系统会根据系统的编码对输入的字节流进行编码,这就解释了为什么utf-8格式的字符串”哈哈”,输出的是”鍝堝搱”,因为 ‘\xe5\x93\x88\xe5\x93\x88’用GB2312去解释,其显示的出来就是”鍝堝搱”。

同时,因为str记录的是字节数组,只是某种编码的存储格式,至于输出到文件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样子。这里再对print进行一点补充说明:当将一个unicode对象传给print时,在内部会将该unicode对象进行一次转换,转换成本地的默认编码(也就是如图中直接输出su编码正常的原因)。

3.3.2 str和unicode对象的转换

str和unicode对象的转换,通过encode和decode实现,具体使用如下:

将GBK’哈哈’转换成unicode,然后再转换成UTF8



3.3.3 操作不同文件的编码格式的文件

建立一个文件test.txt,文件格式用ANSI,内容为: “abc中文”

用python来读取

# coding=gbk
print open("Test.txt").read()
结果:abc中文
把文件格式改成UTF-8:
结果:abc涓枃


显然,如果文件格式不是gbk,需要解码:

# coding=gbk
import codecs
print open("Test.txt").read().decode("utf-8")
结果:abc中文


上面的test.txt我是用Editplus来编辑的,但当我用Windows自带的记事本编辑并存成UTF-8格式时, 运行时报错:

Traceback (most recent call last): File "ChineseTest.py", line 3, in print open("Test.txt").read().decode("utf-8")
UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence


原来,某些软件,如记事本,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。 因此我们在读取时需要自己去掉这些字符,python中的codecs module定义了这个常量:

# coding=gbk
import codecs
data = open("Test.txt").read()
if data[:3] == codecs.BOM_UTF8: data = data[3:]
print data.decode("utf-8")
结果:abc中文


3.3.4文件的编码格式和编码声明的作用

源文件的编码格式和对字符串的声明有什么作用呢?

先说文件编码格式:文件的编码格式决定了在该源文件中字符串的编码格式

而编码声明的作用:即每个文件在最上面的地方的
# coding=gbk
,作用有三:

1. 声明源文件中将出现非ascii编码,通常也就是中文;

2. 在高级的IDE中,IDE会将你的文件格式保存成你指定编码格式。(如pycharm

3. 决定源码中类似于u’哈’这类声明的将’哈’解码成unicode所用的编码格式,也是一个比较容易让人迷惑的地方。

看示例:

#coding:gbk
ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss


将这个些代码保存成一个utf-8文本,运行,你认为会输出什么呢?大家第一感觉肯定输出的肯定是:

u'\u54c8\u54c8' ss:哈哈


但是实际上输出是:

u'\u935d\u581d\u6431' ss:鍝堝搱
```
为什么会这样,这时候,就是编码声明在作怪了,在运行ss = u'哈哈'的时候,整个过程可以分为以下几步:
1.  **获取'哈哈'的编码:由文件编码格式确定**,为'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码形式)
2.  转成 unicode编码的时候,在这个转换的过程中,**对于'\xe5\x93\x88\xe5\x93\x88'的解码,不是用utf-8解码,而是用声明编码处指定的编码GBK**,将'\xe5\x93\x88\xe5\x93\x88'按GBK解码,得到就是''鍝堝搱'',这三个字的unicode编码就是u'\u935d\u581d\u6431',至止可以解释为什么print repr(ss)输出的是u'\u935d\u581d\u6431' 了。

好了,这里有点绕,我们来分析下一个示例:

```python
#!coding=utf-8
ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss

<div class="se-preview-section-delimiter"></div>


将这个示例这次保存成GBK编码形式,运行结果,竟然是:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in

position 0: unexpected code byte


这里为什么会有utf8解码错误呢?想想上个示例也明白了,

1. 转换第一步,因为文件编码是GBK,得到的是’哈哈’编码是GBK的编码’\xb9\xfe\xb9\xfe’

转换成 unicode的时候,会用UTF8对’\xb9\xfe\xb9\xfe’进行解码,而大家查utf-8的编码表会发现,它在utf8编码表中根本不存在,所以会报上述错误。

因此,一切都清楚了—-

总结

notepad++不会根据文件头上的
#!coding
来自动判断或者改变文件的编码方式,这个
#!coding
是给python的解释器看的

2.

对于在notepad++中写python输入中文的问题:

如果是先在win中新建一个txt,然后直接改后缀为py,用notepad++打开,作为python的代码进行编写时, 因为此时note是ANSI编码的,默认不识别中文,必须要加
#!coding=gbk
或者
#!coding=utf-8
什么的都可以,此时在cmd显示端显示的都不是乱码

如果note已经改成了utf-8编码的,python似乎可以直接识别,在前面不用加
#!coding
也可以,不过因为cmd显示端是gbk的编码,如果想要显示不是乱码,可以先.decode(‘utf-8’)转化为unicode或者直接在中文前u’中文’

pycharm打开乱码的问题就是因为之前在notepad++中声明和编码不一致,而pycharm会根据声明来对文档进行编码,至此,还是推荐在win下面统一用gbk编码吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: