背景:在实际数据处理中,我们或多或少会接触到中文,如两个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. 使用kubeadm安装Kubernetes 1.12

    使用kubeadm安装Kubernetes 1.12 https://blog.frognew.com/2018/10/kubeadm-install-kubernetes-1.12.html 测试环 ...

  2. Linux之防火墙与端口

    1.关闭所有的 INPUT FORWARD OUTPUT 只对某些端口开放.下面是命令实现: iptables -P INPUT DROPiptables -P FORWARD DROPiptable ...

  3. String.replaceAll()方法替换字符串中的反斜杠(\)

    replaceAll()方法实际是采用正则表达式的规则去匹配的. 在regex中"\\"表示一个"\",在java中一个"\"也要用&quo ...

  4. Java多线程学习(六)Lock锁的使用

    系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...

  5. Java的四种引用——强弱软虚

    1.强引用—用new 当我们用new向堆区申请一片内存空间时,此时就是强引用. 当内存不足,GC(垃圾收集器)不会回收该强引用的对象. 2.软引用—用SofeReference类实现 用来描述一些还有 ...

  6. 【R语言学习】时间序列

    时序分析会用到的函数 函数 程序包 用途 ts() stats 生成时序对象 plot() graphics 画出时间序列的折线图 start() stats 返回时间序列的开始时间 end() st ...

  7. 【Python学习笔记】Coursera课程《Python Data Structures》 密歇根大学 Charles Severance——Week6 Tuple课堂笔记

    Coursera课程<Python Data Structures> 密歇根大学 Charles Severance Week6 Tuple 10 Tuples 10.1 Tuples A ...

  8. 手動設定 電池溫度 mtk platform

    adb root adb shell echo "3 1 27" > ./proc/mtk_battery_cmd/battery_cmd 27 即是所要設定的溫度, 此設定 ...

  9. java===java基础学习(13)---this,static(静态变量和静态方法)的使用

    package dog; public class PersonAndDog { public static void main(String[] args) { Dogs da_huang = ne ...

  10. 自动化测试===requests+unittest+postman的接口测试

    postman是一个跨平台的接口测试工具,下载链接在这里:https://www.getpostman.com/ unittest是一个单元测试框架,python中安装:pip install uni ...