Python中的字符编码与解码困扰了我很久了,一直没有认真整理过,这次下静下心来整理了一下我对方面知识的理解。

文章中对有些知识没有做深入的探讨,一是我自己也没有去深入的了解,例如各种编码方案的实现方式等;二是我觉得只要提能对理解Python字符编码与解码的关键知识即可,想深入可以查其它资料。

文中的观点肯定有纰漏,只做参考,欢迎指正。

Unicode

参考:http://baike.baidu.com/view/40801.htm

Unicode是什么,这里不多说了,百科上面讲的很清楚了,这里只提下有助于理解本文主题的知识。

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。每个字符都对应一个编号,编号的范围是0-0x10FFFF来。

字符编码方案

参考:http://baike.baidu.com/view/40801.htm

我们知道,每个Unicode字符对应一个编号,例如汉字“我”对应的编号是25105,但在程序中不是直接用编号来表示Unicode字符的(那得有多长的数字啊),而是表示成16进制格式,但具体怎么转换成16进制,不同的编码方案采用的方式不一样。

>>> s = u'我'
>>> ord(s)
25105
>>> s
u'\u6211'
>>> s.encode('utf-8')
'\xe6\x88\x91'
>>> s.encode('utf-16')
'\xff\xfe\x11b'
>>> s.encode('utf-32')
'\xff\xfe\x00\x00\x11b\x00\x00'
>>> s.encode('gbk')
'\xce\xd2'
>>>

我们用unicode()内置函数创建了一个Python中的unicode字符,然后ord()函数可以得到它在Unicode字符集中的编号。Python的unicode对象有一个encode()方法,用来对unicode对像进行编码。

这个示例中的一些知识,在后面会讲到,现在不用深究。这里提下UTF-8编码方式,其它的还没深入研究过,但不妨碍本文的主题。

UTF-8以字节为单位对Unicode编号进行编码。每个字节被转换成一个二位的十六进制数。UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。

Python支持很多的编码方案,包括ascii,utf-8,utf-16,utf32,gbk,gb2312等,完整的列表可以在下面的链接中找到:

http://docs.python.org/2/library/codecs.html#standard-encodings

Python中的字符串

在Python中,str 对象表示所有普通字符串对象,它只能表示ASCII码表中的字符,特点是每个字符占用一个字节,所以也叫做字节字符串(Byte string)。unicode 对象则可以表示所有Unicode字符集中的字符。

s = 'I love python'

u = u'我爱Python'

print isinstance(s, str)

print isinstance(u, unicode)

--输出

True

True

还可以使用 str() 函数 和 unicode() 从一个对象构建字符串,

str(object) 函数返回的结果通常可以通过定义 object 的 str 属性来定制返回的结果。

unicode()函数接受多个参数,与编码格式有关,这在后面会讲到。

Python中的Unicode 转义字符

我们常看到“ \u6211” 这样的字符,用json.dumps(obj)时,如里obj是unicode字符,包含非ASCII码,且ensure_ascii=True,那返回的结果字符串中就包含这种形式。这是个转义字符,表示Unicode字符“我”。但是注意的是,这种转义只在unicode字面量中有效,用print 输出时会自动转为对应的unicode字符。而在str字面量中没有特殊意义。web信息中常会遇到“\u4f60\u597d”类型的字符。首先’\u‘开头就基本表明是跟unicode编码相关的。python里str.decode()和str.encode()为我们提供了解码和编码的方法。其中str.decode('unicode_escape')能将此种字符串解码为unicode字符串。下面是在ubuntu的ipython中的操作,

>>> a = u'你好'
>>> a
u'\u4f60\u597d'
>>> print a
你好
>>> b = '你好'
>>> b
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print b
你好
>>> c = '\u4f60\u597d'
>>>
>>> c
'\\u4f60\\u597d'
>>> print c
\u4f60\u597d
>>>
>>> d = r'\u4f60\u597d'
>>> d
'\\u4f60\\u597d'
>>> print d
\u4f60\u597d
>>> d == c
True
>>> e = c.decode('unicode_escape')
>>> e
u'\u4f60\u597d'
>>> print e
你好
>>>

其实6211是字符‘我’在Unicode字符集中的编号25105的16进制值

字符串字面量和程序中处理的字符串

在源代码中,字符串通常用字面量来表示

u = u'我爱Python'

但字面量是给人看的,程序看到的是对字面量进行处理后的字符,而且 str 字符串和 unicode 字符串的处理方式不一样。

unicode 字符串会将字面量中的非ASCII字符替换成Unicode转义符,但最后的结果是与原字符串等价的。

str类型的字面量会使用设置的编码格式进行编码处理,最后得到的是编码字符串(这点很重要,后面会提到)。编码字符串与原字符串不能等同。

--脚本

-- coding: utf-8 --

s = '我爱Python'

u = u'我爱Python'

print 'encoded str: ', repr(s)

print 'escaped unicode: ', repr(u)

print 'str: ', s

print 'decoded str: ', s.decode('utf-8')

print 'unicode: ', u

--输出

encoded str: '\xe6\x88\x91\xe7\x88\xb1Python'

escaped unicode: u'\u6211\u7231Python'

str: 鎴戠埍Python

decoded str: 我爱Python

unicode: 我爱Python

'\xe6\x88\x91\xe7\x88\xb1Python'是对 s 编码后的编码字符串,直接输出编码字符串会得到不一样的结果,因为实际上,'\xe6' 等被当作16进制转义字符来处理了。要想得到正确结果,需要先解码。

u'\u6211\u7231Python'是转义后的与 u 等价的unicode字符串

编码字符串

编码字符串,是指采用指定的编码格式对字符进行编码后得到的字符串。编码格式有很多中,例如 ascii、utf-8、gbk、gbk2312等。

编码字符串是纯 str 字符串,它表示原字符串的编码结果。直接输出编码字符串可能会与原来的字符串表示的值不一样,除非原来的字符串都是ASCII字符。

--脚本

-- coding: utf-8 --

s = '我'

s1 = '我爱Python'

print len(s)

print repr(s)

print s

print repr(s1)

print s1

--输出

3

'\xe6\x88\x91'



'\xe6\x88\x91\xe7\x88\xb1Python'

鎴戠埍Python

'\xe6\x88\x91' 和 '\xe6\x88\x91\xe7\x88\xb1Python' 就是编码字符串。在编码字符串中类似 '\xe6' 这种字符是Python中的16进制转义字符,被看作是一个字符,而不是4个字符。(Python转义序列:http://docs.python.org/2/reference/lexical_analysis.html#string-literals)

我们可以看到 s 的长度已经是3了,因为这里统计的是编码字符串的长度。

上面的例子有个小细节,字符串只包含单个字符的时候,print语句好像做了解码处理能直接输出正确的结果,但多个字符就会乱码。

这是为什么呢?

开始编码和解码

前面介绍了一些基本知识,现在开始来对字符串进行编码和解码了。

编码:

--脚本

-- coding: utf-8 --

u = u'我爱Python'

print 'encoded[utf-8]: ', repr(u.encode('utf-8'))

print 'encoded[gbk]: ', repr(u.encode('gbk'))

print 'encoded[ascii]: ', repr(u.encode('ascii'))

--输出

encoded[utf-8]: '\xe6\x88\x91\xe7\x88\xb1Python'

encoded[gbk]: '\xce\xd2\xb0\xaePython'

encoded[ascii]:

Traceback (most recent call last):

File "C:\Users\chw\Desktop\encoding.py", line 8, in

print 'encoded[ascii]: ', repr(u.encode('ascii'))

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

在上面的例子中,我们对一个unicode字符串采用了不同编码方式进行了编码,打印编码字符串。最后我们得到了一个错误,是因为ascii编码格式不能编码非ASCII字符。

对于str类型的字面量程序会自动做编码处理,所以就不要再去编一次码了。至于程序会采用何种编码格式要看设置,例如,在前面的脚本中,开头都有一个编码格式声明

-- coding: utf-8 --

这个声明告诉编译器该用什么编码格式处理str类型的字面量。在Python2.6以后的版本好像会根据保存源代码文件的格式来判断编码格式(没有声明的情况下),但先不深究了,总之开头指定编码格式应该是个好习惯。

解码:

str对象提供decode()方法来解码

   --脚本

-- coding: utf-8 --

s = '我爱Python'

print repr(s)

print s

print s.decode('utf-8')

--输出

'\xe6\x88\x91\xe7\x88\xb1Python'

鎴戠埍Python #乱码了

我爱Python

前面我们讲过,str 字面量被自动编码成编码字符串,所以这里的 s 已经是编码后的编码字符串了,因此要输出 s 原来的字符,就需要解码。而开始我们指定了源文件的编码方式为utf-8,所以我们需要用utf-8格式来解码。

IDLE交互环境中的差异

在IDLE交互环境中,Unicode字面量好像不能正确的工作

>>> u = u'我爱Python'
>>> u
u'\xce\xd2\xb0\xaePython'
>>> isinstance(u, unicode)
True
>>> print u
ÎÒ°®Python
>>>

此例中,u实际上是表示'我爱Python'的编码字符串的unicode字符串,而不是'我爱Python'的unicode字符串了。也就是说,先将'我爱Python'编码成编码字符串,然后把编码字符串转换成unicode字符串。

在IDLE 交互环境中创建unicode对象的正确方式应该是下面这样:

>>> u = unicode('我爱Python', 'gbk')
>>> print repr(u)
u'\u6211\u7231Python'
>>> print u
我爱Python

unicode函数的第一个参数指定编码字符串,第二个参数指定这个编码字符串的编码格式。函数在处理中,先用第二个参数指定的编码格式解码第一个参数,根据不同的编码格式,可以直接返回一个unicode字符串。

如果省略第二个参数,unicode函数会将ascii作为默认编码格式(不管是交互环境还是脚本中都是这样)。

第二个参数的值与你的环境配置有关,我在windows下面使用IDLE交互环境,默认的编码是gbk或者是gbk兼容的编码格式。

在脚本中unicode字面量能被解析成正确的unicode字符串,没有IDLE那种令人费解的问题

---脚本

-- coding: utf-8 --

u = u'我爱Python'

print repr(u)

print 'unicode: ', u

---输出

u'\u6211\u7231Python'

unicode: 我爱Python

Python中的编码与解码(转)的更多相关文章

  1. python中的编码与解码

      编码与解码 首先,明确一点,计算机中存储的信息都是二进制的   编码/解码本质上是一种映射(对应关系),比如‘a’用ascii编码则是65,计算机中存储的就是00110101,但是显示的时候不能显 ...

  2. python中的编码和解码

    计算机中常见的编码方式有多种,英文一般是ascii编码,其他有unicode,utf-8,gbk,utf-16等编码. 常见编码方式: ASCII编码:ASCII是早期的编码,包含英文字母.数字和 ...

  3. Python中的编码和解码问题

    关于Python中遇到的中文字符串的读取和输入时总是遇到一堆问题,到现在还不是特别明白,只是有了一个大概率的理解,就是:字符串是用什么编码格式编码的,就用什么编码格式来解码. encode()对字符串 ...

  4. python中base64编码与解码

    在python3中用base64进行编码和解码的时候特别注意: 题目要求: 准备一张.jpg图片,比如:mm.jpg,读取图片数据并通过b85encode加密之后写入到新文件mm.txt文件中,然后读 ...

  5. Python 中 base64 编码与解码

    base64 是经常使用的一种加密方式,在 Python 中有专门的库支持. 本文主要介绍在 Python2 和 Python3 中的使用区别: 在 Python2 环境: Python 2.7.16 ...

  6. python中的编码问题:以ascii和unicode为主线

      1.unicode.gbk.gb2312.utf-8的关系 http://www.pythonclub.org/python-basic/encode-detail 这篇文章写的比较好,utf-8 ...

  7. 【转】【Python】 python中的编码问题报错 'ascii' codec can't decode 及 URL地址获取中文

    1.unicode.gbk.gb2312.utf-8的关系 http://www.pythonclub.org/python-basic/encode-detail 这篇文章写的比较好,utf-8是u ...

  8. python基础系列教程——Python中的编码问题,中文乱码问题

    python基础系列教程——Python中的编码问题,中文乱码问题 如果不声明编码,则中文会报错,即使是注释也会报错. # -*- coding: UTF-8 -*- 或者 #coding=utf-8 ...

  9. 转 python3中SQLLIT编码与解码之Unicode与bytes

    #########sample########## sqlite3.OperationalError: Could not decode to UTF-8 column 'logtype' with ...

随机推荐

  1. Android——初学

  2. WiFi(802.11)基础

    参考: 1. Wireshark数据包分析实战(第2版) 2. wifi技术从了解到熟悉1----概念.802.11协议简述及四种主要物理组件.wifi适配层.wap_supplicant和wap_c ...

  3. HeadFirst 设计模式 04 工厂模式

    除了 new 操作符之外, 还有更多创造对象的方法. 工厂处理创建对象的细节. 这么做的目的是为了抽象, 例如把创建比萨的代码包装进一个类, 当以后实现改变时, 只需修改这个类即可. 利用静态方法定义 ...

  4. ORDER BY 语句用于对结果集进行排序。

    ORDER BY 语句 ORDER BY 语句用于根据指定的列对结果集进行排序. ORDER BY 语句默认按照升序对记录进行排序. 如果您希望按照降序对记录进行排序,可以使用 DESC 关键字.

  5. Unity UGUI 实现简单拖拽功能

    说到拖拽,那必然离不开坐标,UGUI 的坐标有点不一样,它有两种坐标,一种是屏幕坐标,还有一种就是 UI 在Canvas内的坐标(暂时叫做ugui坐标),这两个坐标是不一样的,所以拖拽就需要转换. 因 ...

  6. 【BZOJ】1613: [Usaco2007 Jan]Running贝茜的晨练计划(dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1613 水题dp 设d[i][j]为i分钟疲劳为j d[i][j]=d[i-1][j-1]+a[i] ...

  7. POJ 3181 Dollar Dayz 01全然背包问题

    01全然背包问题. 主要是求有多少种组合.二维dp做的人多了,这里使用一维dp就能够了. 一维的转换方程:dp[j] = dp[j-i] + dp[j];当中i代表重量,j代表当前背包容量. 意思就是 ...

  8. openstacksdk resource2 打印__dict__

    在一个继承resource2的实体里,打印self.__dict__结果是: {'_body': <openstack.resource2._ComponentManager object at ...

  9. php和js实现文件拖拽上传

    Dropzone.js实现文件拖拽上传 http://www.sucaihuo.com/php/1399.html demo http://www.sucaihuo.com/jquery/13/139 ...

  10. Ubuntu16.04 Tomcat9的安装

    1. 从http://tomcat.apache.org/download-90.cgi 下载apache-tomcat-9.0.0.M11.tar.gz 2. 上传到Linux后移动到/opt/to ...