码迷,mamicode.com
首页 > Windows程序 > 详细

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

时间:2016-07-10 18:58:11      阅读:506      评论:0      收藏:0      [点我收藏+]

标签:

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’

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

因此,一切都清楚了—-

总结

  1. 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’中文’
  2. pycharm打开乱码的问题就是因为之前在notepad++中声明和编码不一致,而pycharm会根据声明来对文档进行编码,至此,还是推荐在win下面统一用gbk编码吧

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

标签:

原文地址:http://blog.csdn.net/uselym/article/details/51867056

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!