python编码问题

写python的过程中经常出现各种蛋疼的编码问题,于是通过上网查资料,自己做实验,想彻底搞清楚这个问题。

编码和解码的理解

计算机是不认识字符的,计算机只认识二进制的01串,那么字符要存储在计算机中,首先要做的就是把字符用二进制的01串来表示,这就是所谓的编码(encode);当我们要阅读存储在计算机中的字符时,计算机就需要把二进制的01串转换成我们可以读的字符,这就是解码(decode);所以,我们遇到encode error, 一般是计算机需要把某个字符进行存储时,使用的编码找不到该字符对应的01串,而我们遇到decode error时,一般是计算机读取以01串存储的数据,准备转化成我们可识别的字符时,使用的编码识别不了01串。不论是从字符转化成二进制的01串进行存储,还是从二进制的01串转化成我们可读的字符,都需要一个对应的转化表,也就是哪个字符对应哪个01串,关于这样的对应关系有很多种(比如utf-8, gbk等等),就称为编码方式(简称编码,名词)。显然,每种编码方式可以编码的字符都是有限的,那么一种编码方式可以编码的字符集就称作字符集吧。

如果我们用一种编码方式 A 进行encode,用另一种编码方式B进行decode,以“ 我 ” 这个汉字为例,那么就会出现三种情况:一,A和B所使用的字符集中都可以找到“ 我 ” 这个字符,而且A和B表示“ 我 ” 这个字的01串也一样,所以“ 我 ” 这个字的编码和解码就不会存在问题;二,A和B所使用的字符集中都可以找到“ 我 ” 这个字符,但是A和B表示“ 我 ” 这个字符的01串不一致,这时候A按照自己的编码方式将" 我 “ 存储到计算机中,B按照自己的编码方式解读A对“ 我 ” 这个字表示的01串时,就会出现两种情况,一是可以解释,但是显然解释是错误的,可能会对应到另外的字符,这就称之为乱码,我们会看到一堆无意义的字符,另一种是直接不能解释,这时候程序会直接报错,python中就是decode error; 第三种是B所使用的编码集种直接无法找到“ 我 ”, 其表现和第二种一样。

python中的编码

一、python文件的编码

python文件是由python语言解释器进行解释执行的,默认情况下python解释器对python文件用ascii编码方式进行解码,因此如果python文件中包含中文字符,就会报错,如下面test.py的代码

# main 程序
def main():
    print('hello, world!')

main()

执行python test.py 会得到

 File "test.py", line 1
 SyntaxError: Non-ASCII character '\xe7' in file test.py on line 1, but no       
 encoding declared; see http://python.org/dev/peps/pep-0263/ for details

我们可以看到Non-ASCII character '\xe5' in file,也就是说没有声明编码的情况下,python解释器按照ascii解码的时候不认识'\xe5',现在在这个文件的头部加入编码声明,那么声明为什么编码呢?python解释器读的是这个文件,因此这个文件是按照什么编码存储的就按照什么编码声明,我的编辑器是utf-8编码的,因此我声明为utf-8, 代码如下

# coding: utf-8

# main 程序
def main():
   print('hello, world')

main()

这次再执行这个文件,就正常输出了 “hello, world", 刚才这个编码问题就是我们的编辑器保存代码使用的编码和python解释器解释代码使用的编码不一致导致的,因此我们通过 # coding: utf-8 告诉python解释器应该使用的编码,这个问题就解决了。

二、python中的字符串和unicode

由于我使用的是python2.7, 因此,仅针对python2.7讨论这个问题。python中有str和unicode两种表示字符串的方式,他们均继承自basestring, 但是却是完全不同的两个东东。str可以说并不是真正的字符串,而是已经经过编码的二进制01串,而unicode确实真正的字符串。

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    s='大家好'
    print(len(u))
    print(len(s))

main()

这段代码的输出是3和9,3我们很好理解,本来就是3个汉字;为什么s的长度是9呢?就是因为s是'大家好'这三个汉字已经编码得到的长度为9字节的01串。这三个汉字已经编码了,那么使用什么编码方式呢?就是编辑器所使用的编码方式,在我这儿也就是utf-8, 我们再做一个实验,对这个问题有更加深刻的理解。

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    print(u)

main()

我执行 python test.py,正常输出 “ 大家好 ", 然后我想让这个输出保存到文件中,因此我执行 python test.py > out.txt, 于是问题出现了,输出下面的错误

Traceback (most recent call last):
File "test.py", line 8, in <module>
main()
File "test.py", line 6, in main
print(u)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2:        
ordinal not in range(128)

为什么我直接输出到终端的时候一切正常,当我想重定向到文件的时候出问题了呢?当我直接输出至终端的时候,按照终端所使用的编码来编码‘大家好’这3个字符,而我的终端所使用的编码是utf-8,和我的编辑器所使用的编码一致,所以不存在问题。但是当我把这个输出要重定向至文件的时候,就出问题了,因为文件本身并不规定编码方式,我也没有对这个字符串进行显式编码,因此python将采用默认的编码方式,而python2.7的默认编码是ascii,因此会出现UnicodeEncodeError,搞清楚问题以后,也就有办法解决问题了,一种是我们对这个unicode字符串进行显式编码, 如下所示

# coding: utf-8

# main 程序
def main():
    u=u'大家好'
    print(u.encode('utf-8'))

main()

另一种是改变python的默认编码为我们这个字符串的编码, 如下所示

# coding: utf-8

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

# main 程序
def main():
    u=u'大家好'
    print(u)

main()

一般推荐第一种方式,尽量避免第二种方式,因为第二种方式只有当我们需要处理的编码方式只有一种时有效,如果我们使用多种编码方式,那么仍然会存在问题。我们再看一段代码

# coding: utf-8

# main 程序
def main():
    s='大家好'
    print(s)

main()

对这段代码执行python test.py直接输出在终端和python test.py > out.txt 都可以得到预期效果,这又是为什么呢?这段代码和之前代码的区别是'大家好'这个字符串是str而不是unicode类型。我们知道str类型是已经编码的01串,因此在输出至终端或文件时不需要再进行编码,所以不会出现之前遇到的问题。

三、思考

这样看,貌似使用str类型更方便一些,省去了我们进行显式的编码了,实则恰恰相反,因为这种隐式的编码方式很不利于代码维护,虽然代码暂时很侥幸,很容易的运行通过了,但是日后我们很难搞清楚这个str里面究竟是什么编码,而且我们也看到通过len拿到的字符串长度也存在问题。而使用第一种unicode的方式,虽然我们写代码时需要多费些功夫,在输出时需要显式进行编码,但是这样也明确了这个字符串所采用的编码,同时拿到的字符串长度也是准确的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容

  • 几个基本概念 bit二进制位, 是计算机内部数据储存的最小单位,11010100是一个8位二进制数。一个二进制位只...
    西电大侠阅读 3,562评论 1 8
  • 阅读python源代码的时候不难发现许多文件开头都有这样一行内容: 如果想在python中使用中文,这样的一行声明...
    Fengya阅读 751评论 0 50
  • 什么是编码 任何一种语言、文字、符号等等,计算都是将其以一种类似字典的形式存起来的,比如最早的计算机系统将英文文字...
    随风化作雨阅读 1,534评论 1 2
  • 继上一篇文章字符集和编码详解总结了常见字符编码后,这篇文章会对python中常见的编码问题进行分析和总结。由于py...
    __七把刀__阅读 2,895评论 0 6
  • 2016-05-01 原创 星夏 我们凭什么活着? 家人的期望、自己的理想、该承担的责任? 我不知道千...
    _小夭阅读 480评论 0 0