python学习之unicode编码

python内建的字符串有两种类型:strUnicode,它们拥有共同的祖先basestring。

Unicode也称做万国码,它为每种语言设定了唯一的二进制编码表示方式,提供从数字代码到不同语言字符集之间的映射,从而可以满足跨平台、夸语言之间的文本处理要求。

Unicode编码系统可以分为编码方式实现方式两个层面。

  • 在编码方式上,分为UCS-2和UCS-4两种方式,UCS-2用两个字节编码,UCS-4用4个字节编码。
  • 一个字符的Unicode编码是确定的,但是在实际传输过程中,由于系统平台的不同以及处于节省空间的目的,实现方式有所差异。Unicode的实现方式称为Unicode转换格式,简称为UTF,包括UTF-7、UTF-16、UTF-32、UTF-8等,较为常见的是UTF-8,他的特点是对不同范围的字符使用不同长度的编码,其中0x00 ~ 0x7F的字符的UTF-8编码与ASCII编码完全相同,UTF-8编码的最大长度是4个字节。

str与字节码

 
 
  1. s = "中国北京"

s是个字符串,它本身存储的就是字节码。那么这个字节码是什么格式的?

如果这段代码是在解释器上输入的,那么这个s的格式就是解释器的编码格式,对于windows的cmd而言,就是gbk。

如果将段代码是保存后才执行的,比如存储为utf-8,那么在解释器载入这段程序的时候,就会将s初始化为utf-8编码。

unicode与str

python 在内部使用两个字节来存储一个unicode,使用unicode对象而不是str,好处就是unicode方便于跨平台。

你可以用如下两种方式定义一个unicode:

 
 
  1. s1 = u"中国北京"
  2. s2 = unicode("中国北京", "utf-8")

encode与decode

  • decode()方法讲其他编码对应的字符串解码为Unicode,
  • encode()方法将Unicode编码转换为另一种编码。

我们可以写这样的代码:

 
 
  1. # -*- coding: utf-8 -*-
  2. # su是一个utf-8格式的字节串
  3. su = "中国北京"
  4. # s被解码为unicode对象,赋给u
  5. u = s.decode("utf-8")
  6. # u被编码为gbk格式的字节串,赋给sg
  7. sg = u.encode("gbk")
  8. print sg

看下面这个例子:

 
 
  1. s = "中国北京"
  2. s.encode('gbk')

上面的代码会报错,错误信息:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

原因:当对str进行编码时,会先用默认编码将自己解码为unicode,然后在将unicode编码为你指定编码,而python的默认编码defaultencoding是ascii码,所以会出错。

上面代码等价于:

 
 
  1. s = "中国北京"
  2. # sys.getdefaultencoding() == "ascii"
  3. s.decode(sys.getdefaultencoding()).encode('gbk')

解决方法如下:

 
 
  1. # 重新设置默认编码
  2. reload(sys)
  3. sys.setdefaultencoding('utf-8')
  4. s = "中国北京"
  5. s.encode('gbk')

再比如你使用str创建unicode对象时,如果不说明这个str的编码格式,那么程序也会使用defaultencoding。

 
 
  1. # 两者等价
  2. u = unicode("中国北京")
  3. u = unicode("中国北京", sys.getdefaultencoding())

默认的defaultcoding:ascii是许多错误的原因,所以早早的设置defaultencoding是一个好习惯。

普通字符和Unicode进行字符串连接的时候抛出UnicodeDecodeError异常。

 
 
  1. s = "中文北京" + u"Chinese Test"
  2. print s

原因:使用 + 操作符来进行字符串的连接时,左边为中文字符串,类型为str,右边为Unicode字符串,当两种类型的字符串连接的时候,python将左边的中文字符串转换为Unicode再与右边的Unicode字符串连接,将str转换为Unicode时使用系统默认的ASCII编码对字符串进行编码,就会出现UnicodeDecodeError异常。

解决方法:

  1. 指定str转换为Unicode时的编码方式。
       
       
    1. s = "中文北京".decode('utf-8') + u"Chinese Test"
  2. 将Unicode字符串进行UTF-8编码
       
       
    1. s = "中文北京" + u"Chinese Test".encode("utf-8")

文件头声明编码

一般来说进行源文件编码声明有三种方式:

 
 
  1. #coding=utf-8
  2. # 大多推荐使用此方式
  3. #!/usr/bin/python
  4. # -*- coding: utf-8 -*-
  5. #!/usr/bin/python
  6. # vim: set fileencoding=utf-8:

文件头声明编码的作用:

  • 如果代码中有中文注释,就需要此声明。
  • 比较高级的编辑器(比如我的emacs),会根据头部声明,将此作为代码文件的格式。
  • 程序会通过头部声明,解码初始化 u"中国北京"这样的unicode对象,(所以头部声明和代码的存储格式要一致)。

几点建议

基本设置

  • 主动设置defaultencoding(默认的是ascii)
  • 代码文件的保存格式要与文件头部的# -*- coding:xxx -*- 一致
  • 如果是中文,程序内部尽量使用unicode,而不用str

关于打印
你在打印str的时候,实际就是直接将字节流发送给shell。如果你的字节流编码格式与shell的编码格式不相同,就会乱码。
而你在打印unicode的时候,系统自动将其编码为shell的编码格式,是不会出现乱码的。

程序内外要统一
如果说程序内部要保证只用unicode,那么在从外部读如字节流的时候,一定要将这些字节流转化为unicode,在后面的代码中去处理unicode,而不是str。

 
 
  1. with open("test.txt") as f:
  2. for i in f:
  3. # 将读入的utf-8字节流进行解码
  4. u = i.decode('utf-8')
  5. ....

如果把连接程序内外的这段数据流比喻成通道的的话,那么与其将通道开为字节流,读入后进行解码,不如直接将通道开为unicode的。

 
 
  1. # 使用codecs直接开unicode通道
  2. file = codecs.open("test.txt", "r", "utf-8")
  3. for i in file:
  4. print type(i)
  5. # i的类型是unicode的

所以python处理中文编码问题的关键是你要清晰的明白,自己在干什么,打算读入什么格式的编码,声明的的这些字节是什么格式的,str到unicode是如何转换的,str的一种编码到另一种编码又是如何进行的。 还有,你不能把问题变得混乱,要自己主动去维护一种统一。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值