Python中文社区 ID:python-china
创新互联建站主营播州网站建设的网络公司,主营网站建设方案,手机APP定制开发,播州h5成都微信小程序搭建,播州网站营销推广欢迎播州等地区企业咨询
为解决语言各自为政的编码,人们提出了Unicode编码方案,这个方案简单粗暴:把世界上所有语言字符统一编码。Unicode的两种方案UCS-2和UCS-4,可使用空间分别达到2^16和2^32个:外星人到访地球之前,应该是够用的。
我们看几个字符的Unicode编码码位(code point)是怎样的:
- ls = 'abAB巩'
- print([ord(l) for l in ls])
结果:[97, 98, 65, 66, 24041, 9733, 9734]。可见,字母abAB的Unicode码位和其ASCII码位一致,所以字符为字母时两者兼容,而汉字巩的码位为24041(0x5de9),与之前的GB系列编码47534(0xb9ae)不同,所以Unicode和GB系列编码之间是不完全兼容的:只有ASCII部分兼容。
所有国家的人都使用Unicode编码之后,扩展、乱码问题都不复存在:所有人类语言字符都有了一个统一的编码码位,沟通中我们写出的每个数字编码,都有唯一的字符与他对应。Python中chr函数返回Unicode码位对应的字符。
- >>> print([chr(i) for i in [123,957,24041]])
- ['{', 'ν', '巩']
那么我们可以使用强大的Unicode进行编码了么?
- >>> ls = 'abAB巩'
- >>> ls.encode('Unicode')
- Traceback (most recent call last):
- File "
", line 1, in LookupError: unknown encoding: Unicode
未知编码Unicode!这是因为,并不存在Unicode码这种编码形式,Unicode只是一个码位表,它只是建立了字符和整数之间的映射。至于整数码位(code point)如何存储成字节,先存高位低位,有没有特殊标志,Unicode并不直接决定,而是交给具体编码来考虑这些细节:UTF-32,UTF-16和UTF-8。
UTF-32,顾名思义,是用32位,也就是四个字节来存储一个字符的编码方案。
- >>> 'aA巩'.encode('utf-32LE')
- b'a\x00\x00\x00A\x00\x00\x00\xe9\x5d\x00\x00'
可见,所有的字符,都使用了四个字节来存储:每个字节除了Unicode码位之外,不足用\x00来填充。此法简单明了,Unicode码位不用转换,直接填充。但大量的\x00造成了极大的浪费。有没有办法解决这种浪费了?压缩下用两位行不行?
当用UTF-16来编码时。
- >>> 'aA巩'.encode('utf-16LE')
- b'a\x00A\x00\xe9\x5d'
两个字节对绝大多数Unicode码位来说是够用的,不够用的话系统自动用四位表示。这是系统实现,我们无需关心。UTF-16编码后的字节序列和字符,依然能够一一对应起来。UTF-16其实有两种编码方法,分别为上例的UTF-16LE和如下的UTF-16BE,测试:
- >>> 'aA巩'.encode('utf-16BE')
- b'\x00a\x00A\x5d\xe9'
两者基本一样,只是高低字节位置发生了颠倒。LE和BE后缀,表示小字节序(little endian)和大字节序(big endian)。这是计算机内部关于字节的MSB(大权重字节)放在字节的开头还是结尾的具体实现细节。
《格列佛游记》中,小人国国民为吃鸡蛋先吃大头或小头,针锋相对,组成了两个军事对立集团big endians和little endians,相互间多次发动战争。
那么两个字节就是Unicode编码的极限了么?
能不能用可变数目的字节来存储文本呢?如果存储的是英文文本的话,每个字符只用一个字节就可以;汉字的话,再进行扩展。如此来进一步节省存储空间。答案是可以的,这就是可变长度编码UTF-8。
- >>> 'aA巩'.encode('utf-8')
- b'aA\xe5\xb7\xa9'
这是目前最短的字节序列,因为aA分别存储成了一个字节。需要注意的是,UTF-32和UTF-16中,巩的字节序列是0x5de9,但在UTF-8中,字节序列变成了0xe5b7a9。这说明UTF-8编码不是简单地把Unicode码位直接存储进字节序列中,而是进行了某些转换。这些转换,保证了英文用一位存储,汉语等较大字符多字节存储。那么是如何转换的呢?
本部分过于细节,可略过。UTF-8实现了可变长度的编码,为解码时区分可变长度究竟多长,需要在字节序列里使用特殊模板。UTF-8编码遵循以下规则:
以汉字巩为例,其Unicode码位为0x6c49,二进制位110 1100 0100 1001。位于第三行范围,所以需要三个字节来存储,写出模板,1110 **** 10** **** 10** ****,使用二进制,从右向左填充,不足部分补零,可得结果1110 0110 1011 0001 1000 1001,十六进制为0xe6 0xb7 0x89,所以巩编码为UTF-8的字节序列形式为0Xe6b789。让我们从UTF-8编码转换细节中,回到UTF三种编码的长度问题上来。
以上三种编码方式,由于压缩率不用,导致文件长度也不同,以下程序比较当文本为汉字和英语内容时,三种不同编码的长度:
- es = 'abcdefghij'
- cs = '莫愁前路无知己,天下谁人不识君。'
- codes = ['utf-32le','utf-16le','utf-8']
- print([len(es.encode(code)) for code in codes])
- print([len(cs.encode(code)) for code in codes])
输出为 [40, 20, 10] [64, 32, 48] 可见,对于英文来说,UTF-8比UTF-16和UTF-32编码都要有优势;对汉字来说,最有优势的反而是UTF16编码。这是因为UTF-16编码中,大部分汉字采用2Byte存储,而UTF-8中汉字需要三个字节存储。在日常生活中,因为考虑到最大兼容性,UTF-8使用的最为广泛。至此,我们从ASCII码到GB系列编码,再到Unicode和相应的UTF系列编码,一路进化,拥有了一个包罗万码,不会乱码和有较高压缩率的字符编码系统。可以使用了么?没有!因为我们只是编码了文本自身,并没有记载具体用了那个编码:当我们发送一份文件后,除非告诉对方,否则对方不知道应该该用什么编码打开它。解决这个问题,我们留待下篇文章分析。
当前标题:用Python理清编码问题:Unicode万国码
标题链接:http://www.shufengxianlan.com/qtweb/news40/535140.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联