Pythonunicode编码问题详细讲解
这个问题在python3.0⾥已经解决了。
这有篇很好的⽂章,可以明⽩这个问题:
为什么会报错“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)”?本⽂就来研究⼀下这个问题。
字符串在Python内部的表⽰是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另⼀种编码。
decode的作⽤是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表⽰将gb2312编码的字符串str1转换成unicode编码。
encode的作⽤是将unicode编码转换成其他编码的字符串,如de('gb2312'),表⽰将unicode编码的字符串str2转换成
gb2312编码。
因此,转码的时候⼀定要先搞明⽩,字符串str是什么编码,然后decode成unicode,然后再encode成其他编码
代码中字符串的默认编码与代码⽂件本⾝的编码⼀致。
如:s='中⽂'
如果是在utf8的⽂件中,该字符串就是utf8编码,如果是在gb2312的⽂件中,则其编码为gb2312。这种情况下,要进⾏编码转换,都需要先⽤decode⽅法将其转换成unicode编码,再使⽤encode⽅法将其转换成其他编码。通常,在没有指定特定的编码⽅式时,都是使⽤的系统默 认编码创建的代码⽂件。
如果字符串是这样定义:s=u'中⽂'
则该字符串的编码就被指定为unicode了,即python的内部编码,⽽与代码⽂件本⾝的编码⽆关。因此,对于这种情况做编码转换,只需要直接使⽤encode⽅法将其转换成指定编码即可。
如果⼀个字符串已经是unicode了,再进⾏解码则将出错,因此通常要对其编码⽅式是否为unicode进⾏判断:
isinstance(s, unicode) #⽤来判断是否为unicode
⽤⾮unicode编码形式的str来encode会报错
如何获得系统的默认编码?
#!/usr/bin/env python
#coding=utf-8
import sys
defaultencoding()
该段程序在英⽂WindowsXP上输出为:ascii
在某些IDE中,字符串的输出总是出现乱码,甚⾄错误,其实是由于IDE的结果输出控制台⾃⾝不能显⽰字符串的编码,⽽不是程序本⾝的问题。
如在UliPad中运⾏如下代码:
s=u"中⽂"
print s
会提⽰:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)。这是因为UliPad 在英⽂WindowsXP上的控制台信息输出窗⼝是按照ascii编码输出的(英⽂系统的默认编码是 ascii),⽽上⾯代码中的字符串是Unicode 编码的,所以输出时产⽣了错误。
将最后⼀句改为:de('gb2312')
则能正确输出“中⽂”两个字。
若最后⼀句改为:de('utf8')
则输出:\xe4\xb8\xad\xe6\x96\x87,这是控制台信息输出窗⼝按照ascii编码输出utf8编码的字符串的结果。
unicode(str,'gb2312')与str.decode('gb2312')是⼀样的,都是将gb2312编码的str转为unicode编码
使⽤str.__class__可以查看str的编码形式
>>>>>
python怎么读取py文件le/group/python-cn/browse_thread/thread/be4e4e0d4c3272dd
-
----
python是个容易出现编码问题的语⾔。所以,我按照我的理解写下下⾯这些⽂字。
=⾸先,要了解⼏个概念。=
*字节:计算机数据的表⽰。8位⼆进制。可以表⽰⽆符号整数:0-255。下⽂,⽤“字节流”表⽰“字节”组成的串。
*字符:英⽂字符“abc”,或者中⽂字符“你我他”。字符本⾝不知道如何在计算机中保存。下⽂中,会避免使⽤“字符串”这个词,⽽
⽤“⽂本”来表
⽰“字符”组成的串。
*编码(动词):按照某种规则(这个规则称为:编码(名词))将“⽂本”转换为“字节流”。(在python中:unicode变成str)
*解码(动词):将“字节流”按照某种规则转换成“⽂本”。(在python中:str变成unicode)
**实际上,任何东西在计算机中表⽰,都需要编码。例如,视频要编码然后保存在⽂件中,播放的时候需要解码才能观看。
unicode:unicode定义了,⼀个“字符”和⼀个“数字”的对应,但是并没有规定这个“数字”在计算机中怎么保存。(就像在C中,⼀个整数既
可以是int,也可以是short。unicode没有规定⽤int还是⽤short来表⽰⼀个“字符”)
utf8:unicode实现。它使⽤unicode定义的“字符”“数字”映射,进⽽规定了,如何在计算机中保存这个数字。其它的utf16等都是
unicode实现。
gbk:类似utf8这样的“编码”。但是它没有使⽤unicode定义的“字符”“数字”映射,⽽是使⽤了另⼀套的映射⽅法。⽽且,它还定义了如何在
计算机中保存。
=python中的encode,decode⽅法=
⾸先,要知道encode是 unicode转换成str。decode是str转换成unicode。
下⽂中,u代表unicode类型的变量,s代表str类型的变量。
s.decode('...')经常是会出错的,因为str是什么“编码”取决于上下⽂,当你解码的时候需要确保s是⽤什么编码的。就像,打开zip⽂
件的时候,你要确保它确实是zip⽂件,⽽不仅仅是伪造了扩展名的zip⽂件。
u.decode(),s.encode()不建议使⽤,s.encode相当于s.decode().encode()⾸先⽤默认编码(⼀般是
ascii)转换成unicode在进⾏encode。
=关于#coding=utf8=
当你在py⽂件的第⼀⾏中,写了这句话,并确实按照这个编码保存了⽂本的话,那么这句话有以下⼏个功能。
1.使得词法分析器能正常运作,对于注释中的中⽂不报错了。
2.对于u"中⽂"这样literal string能知道两个引号中的内容是utf8编码的,然后能正确转换成unicode
3."中⽂"对于这样的literal string你会知道,这中间的内容是utf8编码,然后就可以正确转换成其它编码或unicode了。
没有写完,先码那么多字,以后再来补充,这⾥不是wiki,太⿇烦了。
>>>>>
>>>>>
=Python编码和Windows控制台=
我发现,很多初学者出错的地⽅都在print语句,这牵涉到控制台的输出。我不了解linux,所以只说控制台的。
⾸先,Windows的控制台确实是unicode(utf16_le编码)的,或者更准确的说使⽤字符为单位输出⽂本的。
但是,程序的执⾏是可以被重定向到⽂件的,⽽⽂件的单位是“字节”。
所以,对于C运⾏时的函数printf之类的,输出必须有⼀个编码,把⽂本转换成字节。可能是为了兼容95,98,
没有使⽤unicode的编码,⽽是mbcs(不是gbk之类的)。
windows的mbcs,也就是ansi,它会在不同语⾔的windows中使⽤不同的编码,在中⽂的windows中就是gb系列的编码。
这造成了同⼀个⽂本,在不同语⾔的windows中是不兼容的。
现在我们知道了,如果你要在windows的控制台中输出⽂本,它的编码⼀定要是“mbcs”。
对于python的unicode变量,使⽤print输出的话,会使⽤filesystemencoding()返回的编码,把它变成str。
如果是⼀个utf8编码str变量,那么就需要 print s.decode('utf8').encode('mbcs')
最后,对于str变量,file⽂件读取的内容,urllib得到的⽹络上的内容,都是以“字节”形式的。
它们如果确实是⼀段“⽂本”,⽐如你想print出来看看。那么你必须知道它们的编码。然后decode成unicode。
如何知道它们的编码:
1.事先约定。(⽐如这个⽂本⽂件就是你⾃⼰⽤utf8编码保存的)
2.协议。(python⽂件第⼀⾏的#coding=utf8,html中的等)
2.猜。
>>>>>
> 这个⾮常好,但还不是很明⽩
> 将“⽂本”转换为“字节流”。(在python中:unicode变成str)
"最后,对于str变量,file⽂件读取的内容,urllib得到的⽹络上的内容,都是以“字节”形式的。"
虽然⽂件或者⽹页是⽂本的,但是在保存或者传输时已经被编码成bytes了,所以⽤"rb"打开的file和从socket读取的流是基于字节的.
"它们如果确实是⼀段“⽂本”,⽐如你想print出来看看。那么你必须知道它们的编码。然后decode成unicode。"
这⾥的加引号的"⽂本",其实还是字节流(bytes),⽽不是真正的⽂本(unicode),只是说明我们知道他是
可以解码成⽂本的.
在解码的时候,如果是基于约定的,那就可以直接从指定地⽅读取如BOM或者python⽂件的指定coding或者⽹页的meta,就可以正确解码,
但是现在很多⽂件/⽹页虽然指定了编码,但是⽂件格式实际却使⽤了其他的编码(⽐如py⽂件指定了coding=utf8,但是你还是可以保存成ansi--记事本的默认编码),这种情况下真实的编码就需要去猜了
解码了的⽂本只存在运⾏环境中,如果你需要打印/保存/输出给数据库/⽹络传递,就⼜需要⼀次编码过程,这个编码与上⾯的编码没有关系,只是依赖于你的选择,但是这个编码也不是可以随便选择的,因为编码后的bytes如果⼜需要传递给其他⼈/环境,那么如果你的编码也不遵循约定,⼜给下⼀个⼈/环境造成了困扰,于是递归之~~~~
>>>>>
> 主要有⼀条⾮常容易误解:
> ⼀般⼈会认为Unicode(⼴义)统⼀了编码,其实不然。Unicode不是唯⼀的编码,⽽⼀⼤堆编码的统称。但是Windows下Unicode
> (狭义)⼀般特指UCS2,也就是UTF-16/LE
unicode作为字符集(ucs)是唯⼀的,编码⽅案(utf)才是有很多种
>>>>>
将字符与字节的概念区分开来是很重要的。Java ⼀直就是这样,Python也开始这么做了,Ruby 貌似还在混乱当中。
>>>>>
>>>>>
我也说两句。我对编码的研究相对⽐较深⼀些。因为⼯作中也经常遇到乱码,于是在05年,对编码专门做过研究,并在公司刊物上发过⽂章,最后形成了⼀个教材,每年在公司给新员⼯都讲⼀遍。于是项⽬中遇到乱码的问题就能很快的定位并解决了。
理论上,从⼀个字符到具体的编码,会经过以下⼏个概念。
字符集(Abstract character repertoire)
编码字符集(Coded character set)
字符编码⽅式(Character encoding form)
字符编码⽅案(Character encoding scheme )
字符集:就算⼀堆抽象的字符,如所有中⽂。字符集的定义是抽象的,与计算机⽆关。
编码字符集:是⼀个从整数集⼦集到字符集抽象元素的映射。即给抽象的字符编上数字。如gb2312中的定义的字符,每个字符都有个整数和它对应。⼀个整数只对应着⼀个字符。反过来,则不⼀定是。这⾥所说的映射关系,是数学意义上的映射关系。编码字符集也是与计算机⽆关的。unicode字符集也在这⼀层。
字符编码⽅式:这个开始与计算机有关了。编码字符集的编码点在计算机⾥的具体表现形式。通俗的说,意思就是怎么样才能将字符所对应的整数的放进计算机内存,或⽂件、或⽹络中。于是,不同⼈有不同的实现⽅式,所谓的万码奔腾,就是指这个。gb2312,utf-8,utf-16,utf-32等都在这⼀层。
字符编码⽅案:这个更加与计算机密切相关。具体是与操作系统密切相关。主要是解决⼤⼩字节序的问题。对于UTF-16和UTF-32
编码,Unicode都⽀持big-endian 和 little-endian两种编码⽅案。
⼀般来说,我们所说的编码,都在第三层完成。具体到⼀个软件系统中,则很复杂。
浏览器-apache-tomcat(包括tomcat内部的jsp编码、编译,⽂件读取)-数据库之间,只要存在数据交互,就有可能发⽣编码不⼀致,如果在读取数据时,没有正确的decode和encode,出现乱码就是家常便饭了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论