注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

与牛熊共舞

2010与牛熊共舞,共同创造一个神话

 
 
 

日志

 
 

字符编码与python (转)  

2011-01-03 20:25:09|  分类: Python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |


一、编码系统的出现和发展

在 PC 刚开始出现时,只有 ASCII 一种编码系统,因为这种编码系统只包括大小写的英文字母、数字、控制字符等 127 个字符,所以对英语用户是友好的。

随着 PC 在全球的日益普及,各个国家也需要对本国的语言字符进行编码,以方便对包含本国语言的信息进行处理。这其中,大陆出现了 gb2312 等编码系统,台湾,韩国,日本也出现了自己的编码系统。这些编码系统出现的时间比 ASCII 晚,为了兼容 ASCII 码,这些编码系统都在 ASCII 码基础上做了扩展,但是每个编码系统都有自己的数字和字符的映射方式,造成了这些编码系统之间的不兼容性,因此以某种编码系统创建的字符串,如果用另一种编码系统进行解码的话,就会出现乱码。

为了解决编码系统混乱的局面, Unicode 编码系统出现了,它将世界上所有的语言字符和符号都进行统一的编码。既然大家都采用同一种编码系统,自然也就不会混乱了。

 

 

二、字节串和字符串的区别:

字节串 ,顾名思义,就是一个字节的序列,这也是 PC 中数据的最终格式 ( 传输或存储 ) 。字符在计算机中只是一种抽象, 字符串 是这种抽象的序列。

例如,“你好”这两个汉字,呈现在你面前的就是 2 个字符长度的字符串,如果采用 gb2312 编码系统进行保存的话,则是一个 4 个字节长度的字节串,而采用 utf-8 编码进行保存,就是一个 6 个字节长度的字节串。刚开始了解字符编码的概念时,可能会将字节串和字符串混为一谈,明确分清这两个概念后,会对后面的编码系统有更好的理解。

 

 

三、介绍几种常见的编码

ASCII 编码系统,

ASCII是 American Standard Code for Information Interchange 的缩写, 每个ASCII码以1个字节(Byte)储存 ,从0到数字127代表不同的常用符号,例如大写A的ASCII码是65,小写a则是97。由于ASCII只占用一个字节的低七个位,最高位并不使用,所以最高位保持为 0 。

 

gb2312, gbk :

GB2312(1980年)一共收录了7445个字符,由于支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号。2000年的 GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。

从ASCII、GB2312、GBK 到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。 ascii 字符还是以一个字节存储,而非 ascii 字符 ( 如汉字 ) 使用 2 个字节存储。 区分中文编码的方法是高字节的最高位为 1 。GB2312、GBK和GB18030都属于 双字节字符集 (DBCS)。在读取DBCS字节流时,只要遇到高位为1的字节,就可以将该字节和下一个字节作为一个双字节编码,而不用管低字节的高位是什么。

 

Unicode :

在网上关于 Unicode 的资料有很多,比如 http://zh.wikipedia.org/wiki/Unicode ,就不过多描述了。

 

 

四、 Unicode 和其他编码系统的区别

概念上, Unicode 编码系统可分为编码方式和实现方式两个层次。目前实际应用的编码方式是版本UCS-2,即采用16位的编码空间,也就是每个字符占用2个字节;而实现方式采用的是 utf-8 等编码。其他编码系统如 ASCII , gbk 等并不区分编码方式和实现方式。

 

为什么会出现这种差别?

我认为 2 个方面的原因导致了这种差别:

1 占用空间的大小    如果 Unicode 和其他编码系统一样,不区分编码方式和实现方式,统一用 2 个字节表示和保存信息,那么存储和传输全部由 ASCII 字符组成的信息,将要比其他编码系统浪费一倍的空间和时间。如果采用 4 个字节表示一个字符的 UCS-4 标准,浪费的空间会更大。而在这方面,其他编码系统没有这种缺点,因为这些编码系统采用中英文共存的方式进行编码,在这些编码系统中, ASCII 字符还是占用一个字节,而非 ASCII 字符占用 2 个字节。

2 编码方式的识别    目前的 xml , html 和其他一些文档都会在文件的开头用 ASCII 字符标识出编码方式,因此软件在识别此类文件时,会先读取文件头的部分数据(字节串),以 ASCII 编码方式进行解码(转成字符串,因为 ASCII 编码中字节串和字符串完全相同,所以这部分数据可以直接当成字符进行处理),进而找到文档的编码方式,最后以找到的编码方式再对数据进行完整的解码操作。如果按照 2 个字节代表一个字符存储,这种自标识编码系统的方式就无法使用了。

基于以上 2 个原因, Unicode 编码系统分为 2 个层次,在内存操作字符串时,使用 2 个字节表达一个字符的编码方式,但是在存储和传输时,使用 utf-8 等实现方式的编码。 utf-8 编码对 ASCII 字符的编码和其他编码系统一样,只占用一个字节,而对于非 ASCII 字符则需要多个 (>=2) 字节。

 

我们再往深处想一想,按照 Unicode 编码系统的 2 个层次的划分, 不但 utf-8 编码可以作为 Unicode 字符的 存储编码方案 ,其它如 gbk , big5 等编码都可以用做 Unicode 字符的 存储编码方案 ,只不过使用这些编码系统保存 Unicode 字符时,只能保存该编码系统支持的字符集和 Unicode 字符集的交集部分,不像 utf-8 存储编码方案那样,和 Unicode 字符有直接的映射关系,可以保存所有的 Unicode 字符。

但是 utf-8 编码方案也有自己的缺点,虽然在保存 ASCII 字符方面和其他编码系统在占用空间上一样,但是对于非 ASCII 字符,却比其他编码方案要多,例如对简体汉字的“汉”字, gbk 只占用 2 个字节, utf-8 需要使用 3 个字节来保存。

因此目前在 Windows 系统内部使用 Unicode 编码处理字符串,但是在存储上还是采用默认的本地编码方式。比如我使用的 WindowsXP 简体中文版,记事本, word 等程序默认使用 gb2312 编码。而常用的 notepad++ 编辑器,默认的也是 ansi (即 gb2312 编码),但是也提供了 utf-8 格式用于保存文本。

 

 

五、数据编码方式的确定

当用户接收到代表信息的数据 ( 字节串 ) 时,如何确定数据的编码方式呢?

当前了解到的有 3 种方式:


检测,依照某个算法对字节串检索,当其符合某个编码系统的特征时,就以该编码系统对字节串解码,但这种方式的准确率不高,经常会出现乱码的现象;
指定,其中又细分为 2 类:一类是上面描述过的通过在文件开始处以 ASCII 码指定编码方式,另一类是用户了解数据的编码方式,由用户指定应该以何种编码系统对系统进行解码,常见的例子有:浏览器中用户可以指定网页的解码方式,一些编辑器如 notepad++ 也可以让用户指定文件内容的编码方式;

 

 

六、 python 处理字符的方式

python 对字符串的处理分为三个阶段:


在 python2.2 之前,没有对 unicode 的支持,只有 str 数据类型;
从 2.2 版开始,添加了 unicode 数据类型, str 和 unicode 数据类型共存;
在 python 重大的改进版本 3.x 中, str 回归了该数据类型的本意,只表示 字符串 ,和 2.x 版本中的 unicode 数据类型等同;而 字节串 由两种数据类型表示, bytes 表示不可改变的字节串, bytearray 表示可以改变内容的字节串;

 

http://www.woodpecker.org.cn/diveintopython3/strings.html 中详细解释了 python3 中对字符处理的细节,也包括对编码系统原理和演化过程极为精彩的描述,这里就不详述了。因为 python2.x 版本还在大量应用中,因此这里着重讲解 python2.x 对字符串的处理方式。

 

python 2.x 中常用的字符串类型有两种,一种是 str ,另一种是 unicode 。实际上 str 担当着 2 个数据类型的作用: 字节串 和 ascii 字符串 。想一想从文件读出的数据用什么类型表示,你就能理解我的意思了。如果只是处理 ASCII 码的字符串操作, str 足矣;但是一旦碰上非 ASCII 字符串, str 就无能为力了。

举个例子来说明这个问题:在字符串“我 love 中文” ( 采用 gb2312 编码 12bytes , utf-8 编码为 15bytes) 中查找字符串“ ov ”,这相当于在一个字节串中查找一个 2bytes 序列,其中第一个字节值为 82 ,第二个字节值为 89 ,即使这个字节串中包含非 ASCII 字符,因为采用 值相等 的查找方式, str.find() 函数是可以胜任的。

但如果是 str.find(" 中文 ") ,可就麻烦了,如果源字符串和查找子串采用相同的编码方式, str.find() 还可以正常工作,但如果采用了不同的编码方式,就得不到正确的结果了,而且即使采用同一种编码方式,找到了匹配的子串开始位置,但这个位置是以字节作为单位的,并不是以字符为单位的,如果你想作进一步的处理,还要参考这个字符串的编码方式才行。

所以在 python2.x 中,使用 unicode 数据类型才是处理字符串的正确方式,在处理任何字符串之前,先将输入的字节串 (str) 按照指定的编码方式转换成字符串 (unicode) ,然后进行处理,处理的结果按照你想要的编码方式再转换成字节串 (str) 以保存或传输。

简单来说,在 python2.x 中字符串的处理方式就是:解码( decode ) -> 处理 -> 编码( encode ),解码按照指定的编码方式将 字节流转换成字符流 ,编码按照指定的编码方式将 字符流转换成字节流 。 python 提供了 encode 和 decode 两个函数用于字节串和字符串间的转换。

 

 

七、几个涉及字符编码的典型场景

1  python 源代码

如果不指定 python 代码的编码方式, python2.x 默认是按照 ascii 来解释 python 源文件的,如果源代码中包括非 ascii 字符,需要在文件开始处指出 python 的编码方式。唯一的例外是如果 python 代码是以 utf-8 等编码方式保存的,也可以不声明编码方式,具体原因和实现细节有关。

python 编码方式的声明和以 utf-8 编码保存而无需指定编码方式的细节可以参看 PEP-0263 : http://www.python.org/dev/peps/pep-0263/

 

2 输入输出

来自于 console ,文件,网络的输入都是基于字节的,在进行字符串处理之前,需要按照指定的编码方式进行解码。比如,来自新浪的一个网页: http ://news.sina.com.cn 在这个 web page 开头声明了编码方式为 gb2312 ,再继续处理之前,先使用 decode 函数解码为 unicode 字符串: web_page.decode('gb2312') 。

当经过处理的字符串需要保存时,需要按照指定的编码方式进行编码,再保存到文件中,如将一个 xml 文档转换为文件数据保存: fd.write(unicode_xml.encode('utf-8'))

  评论这张
 
阅读(568)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018