背景:在实际数据处理中,我们或多或少会接触到中文,如两个dc pack包的diff。使用python对中文数据 处理难免会遇到编码问题。

python里面主要考虑三种编码:

1、源文件编码:

如果我们在源文件中使用中文注释或中文docstring或中文字符串,如不明确指定应使用哪个中文字符集,解 释器将无法处理我们的程序。这是因为解释器默认程序使用的是ASCII或ISO-8859-1(即LATIN-1)编码。

解决方法是在文件头部使用coding声明(往往紧跟在#!注释行后面):

#coding: gbk
或# coding=gbk
或# -*- coding: gbk -*-

2、内部编码: python内部表示一个字符串有两种方式:一种是普通的str对象(基于字节的字符表示),另一种是unicode字符 串。它们之间可相互转换:

unicode转其他编码形式的str对象(encode):

>>> unicodestring = u"我爱你"
>>> unicodestring
u'\xce\xd2\xb0\xae\xc4\xe3'
>>> unicodestring.__class__ #另外使用isinstance(unicodestring, unicode)也可以查看是否是unicode字符串 >>> utf8string = unicodestring.encode("utf-8")
>>> utf8string
'\xc3\x8e\xc3\x92\xc2\xb0\xc2\xae\xc3\x84\xc3\xa3'
>>> utf8string.__class__ >>> isostring = unicodestring.encode("ISO-8859-1")
>>> isostring
'\xce\xd2\xb0\xae\xc4\xe3'
>>> isostring.__class__ >>> utf16string = unicodestring.encode("utf-16")
>>> utf16string
'\xff\xfe\xce\x00\xd2\x00\xb0\x00\xae\x00\xc4\x00\xe3\x00'
>>> utf16string.__class__

str对象转unicode(decode):

>>> unistring1 = unicode(utf8string, "utf-8")
>>> unistring1
u'\xce\xd2\xb0\xae\xc4\xe3'
>>> unistring1.__class__ >>> unistring2 = unicode(isostring, "ISO-8859-1")
>>> unistring2
u'\xce\xd2\xb0\xae\xc4\xe3'
>>> unistring2.__class__ >>> unistring3 = unicode(utf16string, "utf-16")
>>> unistring3
u'\xce\xd2\xb0\xae\xc4\xe3'
>>> unistring3.__class__
s.decode方法和u.encode方法是最常用的, 
简单说来就是,python内部表示字符串用unicode(其实python内部的表示和真实的unicode是有点差别的,对我们几乎透明,可不考虑),和人交互的时候用str对象。 
s.decode -------->将s解码成unicode,参数指定的是s本来的编码方式。这个和unicode(s,encodename)是一样的。 
u.encode -------->将unicode编码成str对象,参数指定使用的编码方式。 
助记:decode to unicode from parameter 
encode to parameter from unicode 
只有decode方法和unicode构造函数可以得到unicode对象。 
上述最常见的用途是比如这样的场景,我们在python源文件中指定使用编码cp936, 
# coding=cp936或#-*- coding:cp936 -*-或#coding:cp936的方式(不写默认是ascii编码) 
这样在源文件中的str对象就是cp936编码的,我们要把这个字符串传给一个需要保存成其他编码的地方(比如xml的utf-8,excel需要的utf-16) 
通常这么写: 
strobj.decode("cp936").encode("utf-16") 

3、外部编码: 一般不必要为字符串内在的表示担忧; 只有当尝试把Unicode传递给给一些基于字节的函数的时候,Unicode字符的表示 变成一个议题, 比如文件的write方法或网络套接字的send 方法。那时,你必须要选择该如何表示这些(Unicode) 字符为字节。从Unicode码到字节串的转换被叫做编码。同样地,当你从文件,套接字或其他的基于字节的对象 中装入一个Unicode字符串的时候,你需要把字节串解码为(Unicode)字符。

示例:

>>> f = open("/home/spider/atdc/data/alias/case1/pack", "r")
>>> data = f.read()
>>> data.__class__ >>> msg = u""
>>> msg += data
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd5 in position 835: ordinal not in range(128)

msg被初始化为unicode字符串,而data是read函数返回的文件内容,是str对象,msg要粘结data就需要编码转换了:

>>> msg += unicode(data, "ISO-8859-1")

另外跟标准输出打交道时也需要考虑编码转换:

>>> print msg
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'ascii' codec can't encode characters in position 835-844: ordinal not in range(128)

有人建议重设sys的defaultencoding,是否能奏效呢?

>>> import sys
>>> reload(sys) >>> sys.setdefaultencoding('utf8')
>>> print msg
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'ascii' codec can't encode characters in position 835-844: ordinal not in range(128)

哦,跟之前一样的错误。那setdefaultencoding起了什么样的作用呢?继续看下面的示例:

>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> u = u"我爱你"
>>> u
u'\xce\xd2\xb0\xae\xc4\xe3'
>>> u.decode("utf8") #unicode不能用utf8解码?!
Traceback (most recent call last):
File "", line 1, in
File "/home/spider/bin/lib/python2.6/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
>>> reload(sys) >>> sys.setdefaultencoding('utf8')
>>> u.decode("utf8")
u'\xce\xd2\xb0\xae\xc4\xe3'

其 实unicode已经是python支持的最底层的字符串格式了,再对其解码的话就使用sys的defaultencoding对结果进行编码。 而reload之前sys的defaultencoding是ascii,ascii只支持0-127的英文字符串的编码格式,所以无法编码中文。而 reload 之后重设只是将解码后的unicode再用unicode编码,所以结果跟解码前的一样。

另外一些环境变量也会影响python的编码:

export LANG=zh_CN.gbk  #之前LAGN=C
>>> u = u"我爱你"
>>> u
u'\u6211\u7231\u4f60'
>>> print u #这个时候就可以直接print了
我爱你
>>> s = "我爱你"
>>> s
'\xce\xd2\xb0\xae\xc4\xe3'
>>> print s
我爱你
>>> s.decode("gbk") #跟变量u的内容一样,说明此时的unicode是用gbk编码的
u'\u6211\u7231\u4f60'
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> sys.stdout.encoding
'GBK'
>>> sys.getfilesystemencoding()
'GBK'

外部编码更进一步的例子可以参考:http://blog.csdn.net/jiyucn/archive/2008/02/16/2100006.aspx

4、其他话题: (1)插入非法字符:

一个字符串中不一定都是统一编码的,如读取一个网页,head部分是utf8编码,而body部分却可能是gb编码,或者由于 截断的缘故一些字符串已经不完整了,这个时候再做编码操作就可能出错。如:

>>> s = "我爱你"
>>> s
'\xce\xd2\xb0\xae\xc4\xe3'
>>> serr = '\xce\xd2\xb0\xae\xc4\xe3\xa1'
>>> s.decode("gbk")
u'\u6211\u7231\u4f60'
>>> serr.decode("gbk")
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa1 in position 6: incomplete multibyte sequence
gbk无法编码第6个字符0xa1。这个时候我们可以指定decode的第2个参数为"ignore":
>>> serr.decode("gbk", "ignore")
u'\u6211\u7231\u4f60'

使用ignore可以忽视那些错误字符,可选项还有strict(缺省),replace(会替换成一个合适的字符,往往是编码集外 的字符),如:

export LANG=zh_CN.gbk
>>> serr = '\xce\xd2\xb0\xae\xc4\xe3\xa1'
>>> serr.decode("gbk", "replace")
u'\u6211\u7231\u4f60\ufffd'
>>> print serr.decode("gbk", "replace")
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'gbk' codec can't encode character u'\ufffd' in position 3: illegal multibyte sequence
对多数编码集来说字符u'\ufffd'都是非法字符。

(2)编码判断:

A、利用chardet识别编码:

#从标准输入读取字符串,重新编码,输出到标准输出

import sys
import chardet data=sys.stdin.read() incodec=chardet.detect(data)['encoding'] data=data.decode(incodec)
data=data.encode('utf-8')
sys.stdout.write(data)

唯一麻烦的是chardet不是python的标准库,需要自己手动安装。

B、利用系统信息,但会有例外:

import sys
reload(sys)
sys.setdefaultencoding('utf8') class OutputWrapper:
"""进行控制台输出编码转换的封装方案""" input_enc = sys.getdefaultencoding()
output_enc = sys.getfilesystemencoding() def __init__(self, target):
self.target = target
self.buffer = '' def flush(self):
self.target.flush() def write(self, message):
lines = message.split('\n')
lines[0] = self.buffer + lines[0]
self.buffer = lines.pop()
for line in lines:
try:
line = line.decode(self.input_enc).encode(self.output_enc) + '\n'
except:
line = line + '\n'
self.target.write(line) sys.stdout = OutputWrapper(sys.__stdout__)
sys.stderr = OutputWrapper(sys.__stderr__)

局限性:

1、这个方案只是简单的假设至少在字符串的每一行中,采用的是相 同的编码,或者是 sys.getdefaultencoding(), 或者是 sys.getfilesystemencoding()。 2、这个方案假设终端输出采用的编码和本地文件系统采用的编码是一样的(通常如此但总有例外)。

C、自己动手丰衣足食:

def zh2unicode(stri):
"""Auto converter encodings to unicode It will test utf8,gbk,big5,jp,kr to converter"""
for c in ('utf-8', 'gbk', 'big5', 'jp','euc_kr','utf16','utf32'):
try:
return stri.decode(c)
except:
pass
return stri

该函数将含中文字符的str对象转换为unicode,但是不知道哪种编码合适,就遍历地decode。局限性就在于遍历集不 一定全,而且遍历集过大的话会影响性能。

初探python编码的更多相关文章

  1. (转载) 浅谈python编码处理

    最近业务中需要用 Python 写一些脚本.尽管脚本的交互只是命令行 + 日志输出,但是为了让界面友好些,我还是决定用中文输出日志信息. 很快,我就遇到了异常: UnicodeEncodeError: ...

  2. Python 编码简单说

    先说说什么是编码. 编码(encoding)就是把一个字符映射到计算机底层使用的二进制码.编码方案(encoding scheme)规定了字符串是如何编码的. python编码,其实就是对python ...

  3. Python之路3【知识点】白话Python编码和文件操作

    Python文件头部模板 先说个小知识点:如何在创建文件的时候自动添加文件的头部信息! 通过:file--settings 每次都通过file--setings打开设置页面太麻烦了!可以通过:View ...

  4. python编码规范

    python编码规范 文件及目录规范 文件保存为 utf-8 格式. 程序首行必须为编码声明:# -*- coding:utf-8 -*- 文件名全部小写. 代码风格 空格 设置用空格符替换TAB符. ...

  5. 【转】python编码的问题

    摘要: 为了在源代码中支持非ASCII字符,必须在源文件的第一行或者第二行显示地指定编码格式: # coding=utf-8 或者是: #!/usr/bin/python # -*- coding: ...

  6. 【转】python编码规范

    http://blog.csdn.net/willhuo/article/details/49300441 决定开始Python之路了,利用业余时间,争取更深入学习Python.编程语言不是艺术,而是 ...

  7. python 编码 UnicodeDecodeError

    将一个py脚本从Centos转到win运行,出错如下: UnicodeDecodeError: 'gbk' codec can't decode byte 0xff in position 0: il ...

  8. Python编码/文件读取/多线程

    Python编码/文件读取/多线程 个人笔记~~记录才有成长   编码/文件读取/多线程 编码 常用的一般是gbk.utf-8,而在python中字符串一般是用Unicode来操作,这样才能按照单个字 ...

  9. 关于Python编码,超诡异的,我也是醉了

    Python的编码问题,真是让人醉了.最近碰到的问题还真不少.比如中文文件名.csv .python对外呈现不一致啊,感觉好不公平. 没图说个JB,下面立马上图.   我早些时候的其他脚本,csv都是 ...

随机推荐

  1. Activity及Intent

    1.Activity 在一个Android应用程序中,Activity是为用户操作而展示的可视化界面.比如你要打电话,这个时候的拨号界面就是一个Activity,你要发短信给你的女朋友,这个短信窗口就 ...

  2. 【JSP EL】EL表达式获取当前时间(两种方式)

    第一种方式: //先在代码段定义<% long date = new Date().getTime(); request.setAttribute("date", date) ...

  3. zoj 1729 Hidden Password

    Hidden Passwordhttp://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=729 Time Limit: 2 Seconds ...

  4. [Luogu 1640] SCOI2010 连续攻击游戏

    [Luogu 1640] SCOI2010 连续攻击游戏 DP太恶心,回来二分图这边放松一下心智. 这个建图真的是难以想到. 因为要递增啊,属性值放x部,装备放y部,对应连边跑Hungary就好了. ...

  5. Druid连接池及监控在spring中的配置

    Druid连接池及监控在spring配置如下: <bean id="dataSource" class="com.alibaba.druid.pool.DruidD ...

  6. 【51NOD-0】1058 N的阶乘的长度

    [算法]数学 [题解]n!的位数相当于ans=log10(n!)上取整,然后就可以拆出来加了. 可以用log10(i)或log(i)/log(10) 阶乘好像有个斯特林公式…… #include< ...

  7. java分页通用篇

    一.创建分页通用类 package com.dkyw.util; import java.util.List; public class Page<T> { private int tot ...

  8. luaj luaoc 回调函数传递的一些小总结

    问题场景:我们的游戏在支付时,由于第三方支付比较费时,可能在支付的过程中,我们lua写的cocos2dx项目会断网,我们的游戏有自动重连的机制.我就想,如果断线好了以后,支付完成了,那在断网之前传入的 ...

  9. ES6新用法

    ES6 详细参考页面 简介 ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现.一般来说,这两个词是可以互换的. let命令 ES6新增了let命令,用来声明变 ...

  10. java封装示例代码

    package com.imooc; public class Telphone { private float screen; private float cpu; private float me ...