Python字符串编码详解

前言

“字符串”是Python非常常用的一种数据类型,当所有的内容全是英文的时候,世界静好;但是字符串中涉及到中文字符的时候,问题来了…

提到Python的字符串编码,我是这样的
这里写图片描述

不知道多少个夜晚,被Python的编码问题搞的心力交瘁生无可恋。好,现在我就要代表宇宙消灭你!!!

盘古开天辟地

计算机在最初发明的时候,只有127个常用字符被编码到计算机里,也就是26个英文字母大小写、数字、符号等,这种编码称为ASCII码。 每一个ASCII占用1字节的空间。
但是当计算机里需要显示中文的时候,ASCII码就不够用了,所以中国就制订了GB2312编码,将中文编入;同时,如此渊博的中文,一个字节肯定不够表示啊,至少需要2个字节表示。
世界上有上百种语言,日本把日文编入Shift_JIS里,韩国把韩文编到Euc-kr里,所以在多语言混合处理的时候,难免会产生混乱。

秩序初现

于是,Unicode字符编码应运而生。
Unicode编码将各种语言统一到一套编码里,每个字符占用2个字节的空间(非常偏僻的字符需要用到4个),从而保证不会出现乱码问题,现代的操作系统和大多数编程语言都支持Unicode。
譬如,字母’A’的ASCII码是65,二进制是0100 0001;’中’的编码已经超过了ASCII,用Unicode编码是20013,二进制是0100 1110 0010 1101

>>> ord("A")
65
>>> ord('中')
20013
>>> 

而字母’A’使用Unicode编码,只需要在前面补0就可以了,即0000 0000 0100 0001,所以Unicode是兼容ASCII码的。

发展前行

Unicode消除了乱码问题,新的问题又出现了:Unicode编码的字符占用2字节的存储空间,但如果文本全都是英文的话,使用Unicode比ASCII需要多一倍的存储空间,在存储传输上就不太划算。

所以,UTF-8——可变长的Unicode编码出现了。UTF-8编码将英文字母编码为1个字节,中文字符编码为3个字节,而生僻字则可能使用4-6个字节,这样,在传输和存储的过程中则会更加节省空间。

所以,计算机中这些编码的运行过程是这样的:

  • 在计算机内存中,统一使用Unicode编码
  • 当需要存储或传输时,使用encode将Unicode编码为UTF-8,如u'中文'.encode('utf-8')的意思是现有编码为Unicode,encode为参数所指示的编码
  • 当从外存读数据进行运算时,则需要使用decode将UTF-8编码解码为为Unicode编码,如'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')的意思是现有编码为参数所指示的编码,decode为Unicode编码

搞清楚了各类编码的关系,接下来需要看看Python中具体的编码问题。

Python2.7中的字符编码

龟叔开发Python的时候,天下大一统的Unicode字符编码还没出来呢,所以最初的Python只支持ASCII编码。后来,才加入了对Unicode的支持,Unicode字符必须要使用u’….’来表示。

>>> u'中文'.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'
>>> u'ABC'.encode('utf-8')
'ABC'
>>> print '\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
中文

从中可以看出,utf-8编码和Unicode编码中英文字母的显示是一样的,但是如上所述,占用字节不一样,utf-8中占用1字节,而Unicode中占用2字节。

注意:不要使用window下的Python IDLE Shell测试代码,可能是它内部的编码都没有处理好,在cmd命令行中则一切ok。

什么时候会出错呢?

>>> '中文'.encode('utf-8')

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '中文'.encode('utf-8')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd6 in position 0: ordinal not in range(128)

Bomb!是不是很熟悉,其实就是因为没有把Unicode编码使用u’…’包裹起来,导致系统以为它是ASCII编码,但是ASCII编码只有127个,肯定越界了。搞清楚了各大编码的关系,是不是解决问题就显得很简单(笑脸)?

Python3中的字符编码

在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,我们不需要u’….’对Unicode字符进行包裹了。

Python2.7:

>>> '中文'
'\xd6\xd0\xce\xc4'
>>> u'中文'
u'\u4e2d\u6587'
>>> print u'中文'
中文
>>> print '中文'
中文

Python 3.5:

>>> '中文'
'中文'
>>> u'中文'
'中文'

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。Python对bytes类型的数据用带b前缀的单引号或双引号表示:

>>> b'ABC'
b'ABC'
>>> b'ABC'.decode('utf-8')
'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

需要区分b’ABC’与’ABC’,前者是utf-8编码的字节类型,每个字符占用一个字节;而后者是Python的字符串str类型。

在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取。

(By MrHammer 2016-05-12 下午4点 @Ningtu Sunny)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值