欢迎来真孝善网,为您提供真孝善正能量书籍故事!

深入探讨Python字符集与编码处理

时间:11-18 神话故事 提交错误

本篇文章给大家谈谈深入探讨Python字符集与编码处理,以及对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。

2 MBCS

对于英语来说,128个符号编码就足够了,但是对于中文等其他语言来说,显然是不够的。后来,每种语言都开发了自己的一套编码。由于单个字节能够表示的字符太少,而且还需要兼容ASCII编码,所以这些编码都采用了多字节来表示字符,如GBxxx、BIGxxx等,它们的规则是如果第一个字节低于x80,它仍然代表一个ASCII字符;如果在x80之上,则表示一个字符和下一个字节(总共两个字节)。因此,多字节字符集MBCS(Multi-Byte Character Set)出现了。 GB2312、GBK、GB18030、BIG5等编码都属于MBCS。由于MBCS大多使用2字节编码,因此有时也称为DBCS(双字节字符集)。我们在Linux系统中看到,含有汉字的文件的编码往往是CP936,其实就是GBK编码。这个名字的由来是因为IBM曾经发明了一个Code Page的概念来包含这些多字节编码,GBK的编码恰好在第936页,所以被简称为CP936。

GB2312是中国规定的汉字编码,也可以说是简体中文的字符集编码。

GBK 是GB2312 的扩展。除了兼容GB2312外,还可以显示繁体中文和日文假名。

3 Unicode

然后大家都觉得不同的编码太多了,不方便。最好用一组字符来表示所有语言字符,因此出现了Unicode。 Unicode/UCS(Unicode字符集)标准只是一个字符集标准,但它并没有规定字符如何存储和传输。 Unicode 是一种字符集而不是特定的编码。它有三种主要的编码方式:最初的Unicode标准使用2个字节来表示一个字符,编码方案是UTF-16。还有一种编码方案UTF-32,使用4个字节来表示一个字符。后来使用英文字符的国家都觉得不好。原本存储一个字符的原则现在是2个字符,空间增加了一倍,所以采用了UTF-8编码。 UTF-8编码中,英文占1个字节,中文占3个字节。

前面提到,Unicode字符集采用UTF-8、UTF-16等进行编码和存储。那么在这种情况下,计算机如何知道文件是采用哪种编码方式的呢? Unicode规范还定义在每个文件的前面添加一个字符BOM(Byte Order Mark)来表示编码顺序。例如,拌饭中“石头”的UTF-16编码是77F3。采用UTF-16方式存储2个字节,一个字节为77,另一个字节为F3。存储时,如果77在前,如果F3在后,则称为大端模式。反之,则是Little endian方法。

您可能听说过UTF-8 不需要BOM。这不是真的,但大多数编辑器在没有BOM 的情况下将UTF-8 读取为默认编码。即使是记事本,在保存时默认使用ANSI(MBCS),在读取文件时也首先使用UTF-8 测试编码。如果能够成功解码,则使用UTF-8进行解码。记事本这种笨拙的做法造成了一个BUG:如果你新建一个文本文件,输入‘姹姧’,然后使用ANSI(MBCS)保存,再次打开时就会变成‘姹姧’。你不妨尝试一下:)。

现在,我们来看看ASCII 编码和Unicode 编码的区别:ASCII 编码是1 个字节,而Unicode 编码通常是2 个字节。

字母A用ASCII编码为十进制为65,二进制为01000001;

字符0的ASCII编码十进制为48,二进制为00110000。注意字符“0”与整数0不同;

汉字“中”超出了ASCII 编码的范围。 Unicode编码十进制为20013,二进制为01001110 00101101;

你可以猜测,如果使用Unicode编码进行ASCII编码,只需要在前面加0即可。因此,A的Unicode编码为00000000 01000001。计算机内存中统一使用Unicode编码。当需要保存到硬盘或传输时,会转换为UTF-8编码。

使用记事本进行编辑时,从文件中读取的UTF-8字符会转换为Unicode字符并存储在内存中。编辑完成后,保存时将Unicode转换为UTF-8保存到文件中:

粘贴_图像.png

浏览网页时,服务器会将动态生成的Unicode内容转换为UTF-8,然后传输给浏览器:

粘贴_图像.png

所以你在很多网页的源代码上看到类似的信息,说明该网页使用了UTF-8编码。

4 ANSI

另外,另外一个不得不提的就是ANSI。 ANSI 在Windows 系统中非常常见。事实上,ANSI 就是Windows 代码页。此模式根据当前区域设置选择特定编码。如果系统语言环境为简体中文,则使用GBK。编码,繁体中文是BIG5编码,日文是JIS编码。

另外,Windows喜欢将BOM_UTF16_LE编码称为Unicode,而BOM_UTF8则称为UTF-8。也有人说UTF-8不需要BOM来表示。这其实是错误的。这是因为编辑器一般默认使用UTF-8来测试字符编码。如果可以成功解码,则使用UTF-8进行解码。即使最初使用ANSI 保存文件,打开文件时仍然首先使用UTF-8 进行解码。例如,如果使用Windows记事本程序新建一个文件,写入“姹姧”并以ANSI编码保存,然后再次打开该文件,你会发现“姹姧”变成了“汉a”。

1 Python 编码基础

1.1 str和unicode

python中有两种支持string数据类型的数据模型,str和unicode,它们的基类是basestring。例如s="Chinese"是str类型字符串,u=u"Chinese"是unicode类型字符串。 Unicode是通过对str类型的字符串进行解码得到的。 Unicode 也可以编码为str 类型。现在

str --解码--unicode

unicode --encode --str 关于编码和解码,

s.encode可以理解为s.encodeTo()。

s.decode可以理解为s.decodeFrom().encode可以指定一个错误处理程序,防止脚本在执行过程中因为编码问题而被中断。

s.encode("gb18030", "忽略")

s.encode("gb18030", "replace") 严格来说,str大概应该称为字节串,是unicode编码的字节序列,因为对于UTF-8编码的str类型"中文",得到的结果使用len()函数是6,因为UTF-8编码的str类型"Chinese"实际上是"xe4xb8xadxe6x96x87"。

Unicode是真正的字符串,它是通过使用正确的字符编码对字节串str进行解码得到的。对于unicode类型u"Chinese"(实际上是u"u4e2du6587"),使用len()函数得到的结果是2。

1.2 头部编码声明

如果python源代码文件中使用了非ASCII字符,比如中文,那么需要在源代码文件的头中声明源代码字符编码。格式如下:

#-*-coding: utf-8-*-这种格式看起来比较复杂。其实python只检查#、coding、encoding等字符串,可以简写为#coding:utf-8。

2 Python2.x常见编码问题

2.1 头部编码声明和文件编码问题

文件头中的编码语句决定了Python解释器解析源代码中str的编码选择方式。例如,如果头语句是UTF-8编码,那么代码中的s="Chinese",python就会按照UTF-8编码格式进行解析。通过repr(s)可以看到字符编码为"xe4xb8xadxe6x96x87"。如果头中声明的编码是gbk编码,Python会使用s gbk编码分析,结果为"xd6xd0xcexc4"。

需要注意的是,文件本身的编码必须与文件头中声明的编码一致,否则会出现问题。文件本身的编码可以在Linux下使用vim下的命令集fenc查看。如果文件本身的编码是gbk,而源文件头中声明的编码是utf-8,那么源代码中如果有中文就会出现问题,因为中文str是按照gbk存储的编码,python正在解析str。有时我以为是utf-8编码,所以会报SyntaxError: (unicode error) "utf-8" codec can"t Decode byte错误。

2.2 默认编码问题

我们看一下python默认编码导致的问题:

#-*-coding: utf-8 -*-

导入系统

打印sys.getdefaultencoding()

u=u"中国人"

print "repr(u) 是", repr(u)

s="中文"

print "repr(s) 是", repr(s)

u2=s.decode("utf-8")

print "u2 是", repr(u2)

#s2=u.decode("utf-8") #编码错误

#u2=s.encode("utf-8") #编码错误

ASCII码

repr(u) 是u"u4e2du6587"

repr(s) 是"xe4xb8xadxe6x96x87"

u2 是u"u4e2du6587"

回溯(最近一次调用):

文件"C:/Users/kaicz/Desktop/bianma.py",第13行,ins2=u.decode("utf-8")

文件“C:Python27libencodingsutf_8.py”,第16 行,解码

返回codecs.utf_8_decode(输入,错误,True)

UnicodeEncodeError: "ascii" 编解码器无法对位置0-1: 序数不在范围内的字符进行编码(128)

回溯(最近一次调用):

文件"C:/Users/kaicz/Desktop/bianma.py",第14行,inu2=s.encode("utf-8")

UnicodeDecodeError: "ascii" 编解码器无法解码位置0: ordinal not in range(128) 中的字节0xe4 请注意示例中注释掉的2 行代码。对于unicode最好不要直接调用decode,对于str最好不要直接调用encode方法。因为如果直接调用的话,就相当于u.encode(default_encoding).decode("utf-8")。 default_encoding是python的unicode实现中使用的默认编码,即通过sys.getdefaultencoding()获得的编码。如果没有设置If,则默认编码为ascii。如果你的unicode本身超出了ascii编码范围,就会报错。同理,如果直接对str调用encode方法,默认会先对str进行解码,即s.decode(default_encoding).encode("utf-8")。如果str本身是中文且default_encoding为ascii,解码时会出现错误,导致上面两行报出UnicodeEncodeError: "ascii" codec can"t Encodecharacters inposition. 错误和UnicodeDecodeError: "ascii" codec can"t Decode byte0xe4 分别位于位置.错误。

上例中注释掉的两行代码执行起来会报错。当然,如果str或者unicode本身在ascii编码范围之内,就不会有问题。例如,如果s="abc"; s.encode("utf-8"),就不会有问题了。该语句执行后,会返回一个与s的id不同的str。

如果要解决例1中的问题,有两种方法。一种是显式指定编码,如下所示,

#编码:utf-8

u=u"中国人"

print repr(u) # u"u4e2du6587"

s="中文"

print repr(s) # "xe4xb8xadxe6x96x87"

u2=s.decode("utf-8")

print repr(u2) # u"u4e2du6587"

s2=u.encode("utf-8").decode("utf-8") # OK

u2=s.decode("utf8").encode("utf-8") # OK 第二种方法是将python的默认编码改为文件编码格式,如下图(这里需要重新加载sys模块因为python初始化的setdefaultencoding方法后来被删除了):

#编码:utf-8

导入系统

重新加载(系统)

sys.setdefaultencoding("utf-8") #更改默认编码为utf-8

u=u"中国人"

print repr(u) # u"u4e2du6587"

s="中文"

print repr(s) # "xe4xb8xadxe6x96x87"

u2=s.decode("utf-8")

print repr(u2) # u"u4e2du6587"

s2=u.decode("utf-8")

u2=s.encode("utf-8") 市场上的一些库,例如BeautifulSoup,有时会错误地使用ascii来解决中文编码。要纠正此行为,请在文件头中添加,

导入系统

reload(sys) #加载setdefaultencoding方法

sys.setdefaultencoding("utf-8")

2.3 读写文件编码

使用Python的open()方法打开文件时,read()读取的是str,编码为文件本身的编码。调用write()写入文件时,如果参数为unicode,则需要使用指定的编码进行编码。如果write()参数为unicode且未指定编码,则编码后使用python的默认编码。

#编码:utf-8

f=打开("测试文件")

s=f.read()

f.close()

print type(s) #u=s.decode("utf-8") #testfile是utf-8编码

f=打开("测试文件", "w")

f.write(u.encode("gbk")) #以gbk编码写入,testfile为gbk编码

f.close() 另外,python codecs模块提供了一个open()方法,可以指定打开文件的编码。使用该方法打开文件并读取返回的是unicode。写入时,如果写入参数为unicode,则使用打开文件时的编码进行写入。如果是str,会先使用默认编码为unicode,然后写入打开文件的编码(这里需要注意的是,如果str是中文,并且sys.getdefaultencoding()默认编码为ascii的话,则a会报解码错误)。

#编码:gbk

导入编解码器

f=codecs.open("testfile", 编码="utf-8")

u=f.read()

f.close()

print type(u) #f=codecs.open("testfile", "a", 编码="utf-8")

f.write(u) #写入unicode

#写入gbk编码的str并自动进行解码和编码操作

s="中文"

print repr(s) # "xbaxba"

#这里,GBK编码的str会被解码为unicode,然后编码为UTF-8并写入。

#f.write(s) #当默认编码为ascii时,这个会报解码错误。

f.close()

2.4 与编码相关的方法

#coding: GBK

导入系统

导入语言环境

定义p(f):

print "%s.%s(): %s" %(f.__module__, f.__name__, f())

# 返回当前系统使用的默认字符编码

p(sys.getdefaultencoding)

# 返回用于将Unicode 文件名转换为系统文件名的编码

p(sys.getfilesystemencoding)

# 获取默认语言环境并返回源(语言,编码)

p(locale.getdefaultlocale)

# 返回用户设置的文本数据编码

# 文档提到这个函数只返回一个猜测

p(locale.getpreferredencoding)

# xbaxba 是"中文"的GBK编码

# mbcs 是不推荐的编码。这只是一个测试来说明为什么不应该使用它。

在笔者的Windows(区域设置为中文(简体) , 中国))

sys.getdefaultencoding(): ascii

sys.getfilesystemencoding():mbcs

locale.getdefaultlocale():("zh_CN","cp936")

locale.getpreferredencoding(): cp936

"xbaxba".decode("mbcs"): u"u6c49"

3 python开发过程中涉及到的编码

在开发Python程序的过程中,会涉及到三个方面的编码:

Python程序文件的编码; Python程序运行环境(IDE)的编码; Python程序读取外部文件和网页的编码;

3.1 Python程序文件的编码

例如Python2自带的IDE在创建并保存文件时提示:

粘贴_图像.png

这是因为Python2编辑器的默认编码是ASCII,无法识别中文,所以会弹出这样的提示。这就是为什么我们在大多数情况下编写Python2程序时习惯在程序的第一行添加:#coding: utf-8。

3.2 Python程序运行时环境(IDE)的编码

执行以下程序

#编码:utf-8

从硒导入网络驱动程序

驱动程序=webdriver.Firefox()

driver.get("http://www.baidu.com")

# 返回百度页面底部的注册信息

文本=driver.find_element_by_id("cp").text

打印(文本)

driver.close()在windows cmd下执行:

粘贴_图像.png

我们要获取的信息是: 2015 百度使用百度反馈前必读京ICP证030173号

Windows cmd使用cp936,即中文GB2312。 GBK字符集中没有‘’,导致通过GBK解析时出现编码问题。

假设,我也想在cmd下执行这个python程序,那么我可以修改cmd的默认编码类型为utf-8,对应的编码为CHCP 65001(utf-8)。在cmd中输入命令chcp 65001并回车。

粘贴_图像.png

然后将cmd的字体改为“Lucida Console”,然后执行程序即可正确输出。

Paste_Image.png

3.3 Python程序读取外部文件、网页的编码

不适用

3.4 chardet模块

chardet是一个非常优秀的编码识别模块。

通过pip 安装

pip 安装chardet

从chardet 导入检测

a="中文"

检测(一)

{"置信度": 0.682639754276994, "编码": "KOI8-R"}

3.5 Python的字符串,Python对Unicode的支持

由于Python的诞生早于Unicode标准的发布,所以最早的Python只支持ASCII编码。普通字符串“ABC”在Python 中是ASCII 编码的。 Python提供了ord()和chr()函数,可以将字母和相应的数字相互转换:

顺序("A")

65

chr(65)

"一个"

Python 后来添加了对Unicode 的支持。以Unicode表示的字符串用u"."表示,如:

打印你"中文"

中国人

你"中"

你"u4e2d"

len(u"中")

1

写u"中" 与u"u4e2d" 相同。 u 后面是十六进制Unicode 吗?因此,u"A"和u"u0041"也是相同的。

如何将两个字符串相互转换?虽然字符串"xxx" 是ASCII 编码的,但也可以看作是UTF-8 编码的,而u"xxx" 只能用Unicode 编码。使用encode("utf-8")方法将u"xxx"转换为UTF-8编码的"xxx":

u"ABC".encode("utf-8")

"ABC"

u"中文".encode("utf-8")

"xe4xb8xadxe6x96x87"

英文字符转换后表示的UTF-8值与Unicode值相等(但占用的存储空间不同),而汉字转换后,1个Unicode字符将变成3个UTF-8字符,而xe4你see 是其中一个字节,因为值为228,没有对应的字母可以显示,所以字节数以十六进制显示。 len() 函数可以返回字符串的长度:

len(u"ABC")

3

len("ABC")

3

len(u"中文")

2

len("xe4xb8xadxe6x96x87")

6

依次将UTF-8编码表示的字符串"xxx"转换为Unicode字符串u"xxx",使用decode("utf-8")方法:

"abc".decode("utf-8")

你"abc"

"xe4xb8xadxe6x96x87".decode("utf-8")

你"u4e2du6587"

print "xe4xb8xadxe6x96x87".decode("utf-8")

中文由于Python源代码也是一个文本文件,当你的源代码

码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按照UTF-8编码读取文件,我们通常在文件开头写上这两行: #!/usr/bin/env python # -*- coding: utf-8 -*-第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释; 第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。 如果你使用Notepad++进行编辑,除了要加上# -- coding: utf-8 --外,中文字符串必须是Unicode字符串: Paste_Image.png 申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保Notepad++正在使用UTF-8 without BOM编码: Paste_Image.png 如果.py文件本身使用UTF-8编码,并且也申明了# -- coding: utf-8 --,打开命令提示符测试就可以正常显示中文: Paste_Image.png

4 实际问题

相信很多用Sublime Text来写Python2的同学都遇到过一下这个问题:在Sublime Text里用Ctrl+B运行代码print u"中文",想要打印出unicode类型的字符串时,会出现以下报错,UnicodeEncodeError: "ascii" codec can"t encode characters in position 0-1: ordinal not in range(128)。 分析: Python在向控制台(console)print的时候,因为控制台只能看得懂由bytes(字节序列)组成的字符串,而Python中"unicode"对象存储的是code points(码点),因此Python需要将输出中的“unicode”对象用编码转换为储存bytes(字节序列)的"str"对象后,才能进行输出。 而在报错里看到UnicodeEncodeError, 那就说明 Python 在将 unicode 转换为 str 时使用了错误的编码。而为什么是 "ascii" 编码呢?那是因为 Python 2 的默认编码就是 ASCII,可以通过以下命令来查看 Python 的默认编码。 >>>import sys >>>print sys.getdefaultencoding() ascii所以此时在Sublime Text里运行print u"中文",实际上等于是运行了, print u"中文".encode("ascii")ASCII编码无法对unicode的中文进行编码,因此就报错了。 那为什么同样的代码 print u"中文" 在 Mac 的终端里却能正常输出中文,难道是因为终端下的 Python 2 的默认编码不是 ASCII?非也,在终端下运行 sys.getdefaultencoding() 结果一样是 ascii。那同样是 ascii 为什么会有不同的结果?难倒这里 Python 用了另外一个编码来转换? 是的,其实 Python 在 print unicode 时真正涉及到的是另一组编码:stdin/stdout/stderr 的编码,也就是标准输入、标准输出和标准错误输出的编码。可以通过以下命令来查看,这里是在我的终端下运行的结果: >>>import sys >>>print sys.stdin.encoding UTF-8 >>>print sys.stdout.encoding UTF-8 >>>print sys.stderr.encoding UTF-8在正常情况下,Python2在print unicode时用来转换的编码并不是Python的默认编码sys.getdefaultencoding(),而是sys.stdout.encoding所设的编码。 因为在我的终端下 Python 的 sys.stdout.encoding 编码是 UTF-8,所以在终端里运行 print u"中文" 时,实际上是等于运行了: print u"中文".encode("UTF-8")编码正确,运行正常,因此没有报错。 在类 UNIX 系统下,Python 应该是通过环境变量 LC_CTYPE 来判断 stdin/stdout/stderr 的编码的。因此一般只要将 shell 的 LANG 环境变量设置对为.UTF-8 后,应该就能在终端里直接 print 出 unicode 类型的字符串了,而不需要在 print 时手动加上 .encode("utf-8") 进行编码了。 但在 Sublime Text 里事情就没那么美好了。在 Sublime Text 里运行查看 stdout 编码的命令,发现: import sys print sys.stdout.encoding ----------------------------- None [Finished in 0.1s]结果甚至不是 "ascii" 而是 None。可能是因为 Sublime Text 的 Build System 是用 subprocess.Popen 来运行 Python 的,导致 Python 无法判断出正确的 stdin/stdout/stderr 编码,于是都变成 None 了。 这种情况也发生在输出的目标是管道的情况下: $ python -c "import sys; print sys.stdout.encoding" | tee /tmp/foo.txt None那么在这种 sys.stdout.encoding 为 None 情况下的 print unicode 怎么办呢?答案就是 Python 只能很无奈地使用 sys.getdefaultencoding() 的默认编码 ascii 来对 unicode 进行转换了。这样就出现了本文开头所说的那个 UnicodeEncodeError 问题。

总结

总结一下Python 2向控制台print输出时的流程: Python启动时,当它发现当前的输出是连接到控制台的时候,它会根据一些环境变量,例如环境变量LC_CTYPE,来设法判断出sys.stdin/stdout/stderr.encoding编码值。当Python无法判断出所需的编码时,它会将sys.stdin/stdout/stderr.encoding的值设置为None。print时判断字符串是否是unicode类型。如果是的话,并且sys.stdout.encoding不为None时,就使用sys.stdout.encoding编码对unicode编码成str后输出。如果sys.stdout.encoding为None的话,就使用sys.getdefaultencoding()默认编码来对unicode进行转换成str后输出。if sys.stdout.encoding: print unicode.encode(sys.stdout.encoding) else: print unicode.encode(sys.getdefaultencoding())

解决方法

解决方法1:

先说最不正确的解决方法: 在文件头部加上, import sys reload(sys) sys.setdefaultencoding("utf-8")这种方法通过dirty hack的方式在Python刚刚启动时更改了Python的默认编码为utf-8。此后: >>>print sys.getdefaultencoding() utf-8但就本文所讨论的问题来说,这个方法并不是真正地直接解决了问题。就如上所说,Python只是在sys.stdout.encoding为None时,才会使用默认编码来转换需要print的unicode字符串。那万一在sys.stdout.encoding存在,但为ascii的情况下呢?这样即使更改了Python的默认编码,同样还是会出现UnicodeEncodeError报错。所以对本问题来说,这个方法治标不治本。 除此之外,很多人都用这个方法来解决Python 2下遇到的其它各种各样的编码问题。但实际上很多大牛都不推荐用这个方法来解决Python 2的编码问题,这里引用下StackOverflow 相关回答里的一句话: the use of sys.setdefaultencoding() has always been discouraged为什么这个方法不被推荐呢?我们来看下Python文档里对这个function是怎么说的: This function is only intended to be used by the site module implementation and, where needed, by sitecustomize. Once used by the site module, it is removed from the sys module"s namespace.可以看到这个方法原本就不是面向用户的方法,并没有打算让用户用这个方法来更改Python 2的默认编码。 那为什么不建议我们更改 Python 的默认编码呢?这里引用 Python 核心开发者、Python Unicode 支持的设计者和实现者:Marc-André Lemburg,他在一个邮件列表上的回复: The only supported default encodings in Python are: Python 2.x: ASCII Python 3.x: UTF-8 If you change these, you are on your own and strange things will start to happen. The default encoding does not only affect the translation between Python and the outside world, but also all internal conversions between 8-bit strings and Unicode. Hacks like what"s happening in the pango module (setting the default encoding to "utf-8" by reloading the site module in order to get the sys.setdefaultencoding() API back) are just downright wrong and will cause serious problems since Unicode objects cache their default encoded representation. Please don"t enable the use of a locale based default encoding. If all you want to achieve is getting the encodings of stdout and stdin correctly setup for pipes, you should instead change the .encoding attribute of those (only). -- Marc-Andre Lemburg eGenix.com从此可见,Python 2 唯一支持的内部编码只有 ASCII,更改其默认编码为其它编码可能会导致各种各样奇怪的问题。在这里他也说了使用 sys.setdefaultencoding() 的方法是彻彻底底的错误,正确的方法应该是更改 stdout 和 stdin 的编码。 所以这个方法是最不正确的填坑方法,请大家慎用。

解决方法 2:

然后说说应当是姿势最正确的,也是大家都懂的方法: 在 print的时候显式地用正确的编码来对 unicode 类型的字符串进行encode("正确的编码")为 str 后, 再进行输出。而在 print的时候,这个正确的编码一般就是 sys.stdout.encoding的值。但也正如上述所说,这个值并不是一直是可靠的,因此需要根据所使用的平台和控制台环境来判断出这个正确的编码。而在 Mac 下这个正确的编码一般都是 utf-8,因此若不考虑跨环境的话,可以无脑地一直用 encode("utf-8") 和 decode("utf-8") 来进行输入输出转换。 在我的经验中,这个策略也是解决 Python 2 其它 unicode 相关编码问题的最佳方法。在PyCon 2012 的一个演讲中(关于 Python Unicode 问题很好的一个演讲,这里有演讲稿的中文翻译版),对这个方法有一个很形象的比喻: Paste_Image.png 因为在程序中进进出出的只有存储 bytes(字节序列)的 str。因此最好的策略是将输入的 bytes 马上解码成 unicode,而在程序内部中均使用 unicode,而当在进行输出的时候,尽早将之编码成 bytes。 也就是要形成一个 Unicode 三明治(如图), bytes 在外,Unicode 在内。在边界的地方尽早进行 decode 和 encode。不要在内部混用 str 和 unicode,尽可能地让程序处理的字符串都为Unicode。

解决方法 3:

虽然解决方法2是最正确的方式,但是有时候在Sublime Text里调试些小脚本,实在是懒得在每个print语句后面写一个尾巴.encode("utf-8")。那么有没有办法能让Sublime Text像在终端里一样直接就能print u"中文"呢?也就是说能不能解决sys.stdin/stdout/stderr.encoding为None的情况呢? 答案肯定是有的,一种方法是用类似更改默认编码的方法一样,用 dirty hack 的方式在 Python 代码中去显式地更改sys.stdin/stdout/stderr.encoding 的值。一样是不推荐,我也没尝试过,在这里就不详说了。 另一种方法则是通过设置 PYTHONIOENCODING 环境变量来强制要求 Python 设置 stdin/stdout/stderr 的编码值为我们想要的,这是一个相对比较干净的解决方法。见文档: PYTHONIOENCODING Overrides the encoding used for stdin/stdout/stderr, in the syntax encodingname:errorhandler. The :errorhandler part is optional and has the same meaning as in str.encode(). New in version 2.6.在 Mac 下对全局 GUI 程序设置环境变量的方法是:使用 launchctl setenv<, ...>命令对所有 launchd 启动的未来子进程设置环境变量。 在这里顺便科普下,为什么对所有 launchd 启动的未来子进程设置环境变量可以使得对 Mac 下所有 GUI 程序生效。这是因为 launchd 是 OS X 系统启动后运行的第一个非内核进程。我们可以在 activity monitor(活动监视器)里看到,它的 pid 是很帅气的 1。而之后所有的进程都将是它的子进程。另外还可以通过 launchd 在 Mac 下实现类 crontab 的功能。 launchctl setenv命令设置的全局环境变量会在电脑重启后失效,因此就需要通过上面说的 launchd 的开机启动任务的功能来在重启后再设置一遍环境变量,其配置方法可以参考这里。也因为这个原因,我并没有使用这个方法来设置 PYTHONIOENCODING环境变量。 而 Sublime Text 提供了一个设置 Build System 环境变量的方法,这个方法各平台的 Sublime Text 都适用。设置 Sublime Text 的 Python Build System 环境变量的步骤如下:将 Sublime Text 默认的 Python Build System 的配置文件Python.sublime-build(找到这个文件的最好方法是安装插件PackageResourceViewer)复制一份到 Sublime Text 的 /Packages/User 文件夹下(在 Mac 和 Sublime Text 3 下这个路径是 ~/Library/Application Support/Sublime Text 3/Packages/User)。打开编辑新复制来的 Python.sublime-build 文件,如下加上一行设置 PYTHONIOENCODING环境变量为 UTF-8 编码的内容,并保存:{ "shell_cmd": "python -u "$file"", "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)", + "env": {"PYTHONIOENCODING": "utf8"}, "selector": "source.python" }这样一来终于在这么长的文章后能在 Sublime Text 里直接运行 print u"中文",而不用再出现万恶的 UnicodeEncodeError 了。 既然都研究到这了,不妨我们试试把 PYTHONIOENCODING 设置成其它编码看看会出现什么情况,例如设置成简体中文 Windows 的默认编码cp936:"env": {"PYTHONIOENCODING": "cp936"} import sys print sys.stdout.encoding print u"你好" ---------------------------------- cp936 [Decode error - output not utf-8] [Finished in 0.1s][Decode error - output not utf-8],这就是 Sublime Text 在 Windows 下可能会出现的问题。这是因为 Sublime Text 的 Build System 默认是用utf-8 编码去解读运行的输出的,而我们指定了让 Python 用 cp936 编码来生成 str 字符串进行输出,那么就会出现 Sublime Text 无法识别输出的情况了。同样在对终端 export PYTHONIOENCODING=cp936后,在终端下 print u"你好" 输出的就会是 ���这样的乱码解决办法之一就是同样在 Python.sublime-build 文件里设置 "env": {"PYTHONIOENCODING": "utf8"}来使得输出统一为 utf-8。或者是更改 Sublime Text 的 Build System 所接受的输出编码,将其改为一致的 cp936 编码,同样也是更改 Python.sublime-build 文件,加入一行: { "shell_cmd": "python -u "$file"", "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)", + "encoding": "cp936", "selector": "source.python" }那我们再试试把这两个设置同时都加到 Python.sublime-build 文件里,也就是让 Python 输出 utf8 编码的字符串,而让 Sublime Text 用 cp936 编码来解读,看看会发生什么情况? { "shell_cmd": "python -u "$file"", "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)", + "env": {"PYTHONIOENCODING": "utf8"}, + "encoding": "cp936", "selector": "source.python" }print u"你好" ---------------------- 浣犲ソ [Finished in 0.1s]笑,居然不是 [Decode error - output not cp936],而是这么喜感的 “浣犲ソ”! 这是因为 “你好” 的 utf-8 编码刚好和 “浣犲ソ” 的 cp936 编码重合了,都是 "xe6xb5xa3xe7x8axb2xe3x82xbd",所以使用 cp936 编码去解读的 Sublime Text 就认为这段字符串就是 “浣犲ソ” 而显示了出来。 >>>print repr("浣犲ソ") # cp936 编码 "xe6xb5xa3xe7x8axb2xe3x82xbd" >>>print repr(u"你好".encode("utf-8")) # utf-8 编码 "xe6xb5xa3xe7x8axb2xe3x82xbd"

5 QA

1.1 请教个问题,文件头部定义的编码#--coding: utf-8 -- 和sys.setdefaultencoding()定义的编码是一回事吗?

-coding:utf-8--指明"当前文件"的encoding。sys.getdefaultencoding()是python默认把字符转换为unicode的encoding。举个例子,假如你的某个源代码文件里有韩文,并且由于不知什么原因,源代码必须用韩文euc-kr。这时你得在源文件开头声明#coding: euc-kr。 然后这个文件里有这么一段代码: a = b"hello" b = u"#*%韩%#@#%文!@#$字$%符" c = a + b虚拟机在执行"c = a + b"这句的时候,发现a是一个byte string,b是一个unicode string。它就会用default encoding先去decode一下a,再去和b进行拼接。所以实际执行的是"c = a.decode(DEFAULT_ENCODING) + b"。 其中的DEFAULT_ENCODING在python2下默认是"ascii",可以用reload(sys): sys.setdefaultencoding("utf-8")改成"utf-8",在python3下默认就是"utf-8"。

END,本文到此结束,如果可以帮助到大家,还望关注本站哦!

用户评论

我一个人

Python里各种字符集太 confusing 了,总搞不清哪个适合哪种情况。

    有5位网友表示赞同!

醉红颜

最近在处理一些汉字数据,用到《变量》Python中的字符集和编码这篇文章了解一下!

    有6位网友表示赞同!

情深至命

学习 Python 写爬虫,对编码格式一定要重视.

    有15位网友表示赞同!

南宫沐风

以前没太在意字符集和编码区别,看了这篇文章突然感觉很关键啊!

    有12位网友表示赞同!

断秋风

终于明白 ASCII 和 Unicode 的关系了!谢谢作者分享.

    有15位网友表示赞同!

闲肆

Python 处理中文字符 really 复杂啊,看来得好好学学。

    有16位网友表示赞同!

隔壁阿不都

这篇文章给我开了一扇新的大门,原来 Python 写代码还能这么讲究字符集!

    有20位网友表示赞同!

尘埃落定

下次写程序就注意用对编码格式啦!

    有10位网友表示赞同!

凝残月

看了《变量》Python中的字符集和编码这篇之后,感觉自己对 Python 有了更高的认识!

    有10位网友表示赞同!

最怕挣扎

学习编程还是要不断积累知识,这篇文章很棒!

    有6位网友表示赞同!

容纳我ii

原来编码能影响程序的运行效果啊,以后要更加仔细。

    有11位网友表示赞同!

非想

希望能再看到一些具体的案例,这样理解会更深刻!

    有11位网友表示赞同!

哭着哭着就萌了°

学习 Python 的朋友们都要认真看《变量》Python中的字符集和编码这篇!

    有8位网友表示赞同!

代价是折磨╳

这篇文章内容很全面,讲解也比较清晰!

    有10位网友表示赞同!

墨城烟柳

我之前遇到过因为编码问题导致程序出错的经历,现在看来是处理没有正确理解啊.

    有13位网友表示赞同!

♂你那刺眼的温柔

太赞了,《变量》Python中的字符集和编码 这篇真是很有用!

    有11位网友表示赞同!

一生只盼一人

分享这篇文章给我的 Python 学习伙伴们!

    有14位网友表示赞同!

青袂婉约

以后遇到类似的问题就回顾一下这篇文吧!

    有7位网友表示赞同!

经典的对白

希望作者能继续分享更多关于 Python 的好文章!

    有18位网友表示赞同!

【深入探讨Python字符集与编码处理】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活