您现在的位置是:亿华云 > 系统运维
用 Python 理清编码问题:Unicode万国码
亿华云2025-10-08 23:16:07【系统运维】9人已围观
简介Python中文社区 ID:python-chinaUnicode——万国码为解决语言各自为政的编码,人们提出了Unicode编码方案,这个方案简单粗暴:把世界上所有语言字符统一编码。Unicode的
Python中文社区 ID:python-china
Unicode——万国码
为解决语言各自为政的清编编码,人们提出了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 "<stdin>", line 1, in <module> LookupError: unknown encoding: Unicode未知编码Unicode!这是因为,并不存在Unicode码这种编码形式,Unicode只是一个码位表,它只是建立了字符和整数之间的映射。至于整数码位(code point)如何存储成字节,先存高位低位,有没有特殊标志,Unicode并不直接决定,而是交给具体编码来考虑这些细节:UTF-32,UTF-16和UTF-8。
UTF-32 四字节为单位
UTF-32,顾名思义,是用32位,也就是四个字节来存储一个字符的编码方案。
>>> aA巩.encode(utf-32LE) ba\x00\x00\x00A\x00\x00\x00\xe9\x5d\x00\x00可见,所有的字符,都使用了四个字节来存储:每个字节除了Unicode码位之外,不足用\x00来填充。此法简单明了,Unicode码位不用转换,直接填充。源码下载但大量的\x00造成了极大的浪费。有没有办法解决这种浪费了?压缩下用两位行不行?
UTF-16 两字节为单位
当用UTF-16来编码时。
>>> aA巩.encode(utf-16LE) ba\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(大权重字节)放在字节的开头还是结尾的具体实现细节。
《格列佛游记》中,小人国国民为吃鸡蛋先吃大头或小头,针锋相对,b2b供应网组成了两个军事对立集团big endians和little endians,相互间多次发动战争。
那么两个字节就是Unicode编码的极限了么?
UTF-8 变长字节编码
能不能用可变数目的字节来存储文本呢?如果存储的是英文文本的话,每个字符只用一个字节就可以;汉字的话,再进行扩展。如此来进一步节省存储空间。答案是可以的,这就是可变长度编码UTF-8。
>>> aA巩.encode(utf-8) baA\xe5\xb7\xa9这是目前最短的字节序列,因为aA分别存储成了一个字节。需要注意的是,UTF-32和UTF-16中,巩的字节序列是0x5de9,但在UTF-8中,字节序列变成了0xe5b7a9。这说明UTF-8编码不是简单地把Unicode码位直接存储进字节序列中,而是进行了某些转换。这些转换,保证了英文用一位存储,汉语等较大字符多字节存储。那么是如何转换的呢?
UTF-8 编码转换规则
本部分过于细节,可略过。UTF-8实现了可变长度的编码,为解码时区分可变长度究竟多长,需要在字节序列里使用特殊模板。UTF-8编码遵循以下规则:
0x00-0x7F之间的码位,兼容ASCII码,单字节直接存储在以下模板 0*** **** 0x80-0x7ff之间,使用两个字节存储,字节模板是110* **** 10** **** 0x800-0xffff之间,使用三个字节存储,字节模板是1110 **** 10** **** 10** **** 0x10000-0x1fffff之间,使用四个字节存储,字节模板是1111 0*** 10** **** 10** **** 10** ****以汉字巩为例,其Unicode码位为0x6c49,二进制位110 1100 0100 1001。位于第三行范围,所以需要三个字节来存储,写出模板,1110 **** 10** **** 10** ****,使用二进制,从右向左填充,不足部分补零,可得结果1110 0110 1011 0001 1000 1001,十六进制为0xe6 0xb7 0x89,所以巩编码为UTF-8的字节序列形式为0Xe6b789。让我们从UTF-8编码转换细节中,回到UTF三种编码的长度问题上来。
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系列编码,一路进化,拥有了一个包罗万码,不会乱码和有较高压缩率的字符编码系统。可以使用了么?没有!因为我们只是编码了文本自身,并没有记载具体用了那个编码:当我们发送一份文件后,除非告诉对方,否则对方不知道应该该用什么编码打开它。解决这个问题,我们留待下篇文章分析。
总结
Unicode统一了世界各语言字符。Unicode几种编码形式中; UTF-32简单,但浪费严重。 UTF-16使用两个字节为单位存储,节省了空间。 UTF-8使用一个字节直接存储,是效率、空间的平衡。很赞哦!(9)
相关文章
- 国内域名
- .net 适用于从事Internet相关的网络服务的机构或公司
- 公司和个人选域名方法一样吗?有什么不同?
- 第三,.cc域名域名也有很多优势资源域名,从整体注册基数也可以由此推断;
- 打开https://www.aizhan.com/输入自己想要查询的域名然后按回车键,如果做过网站都会有数据显示出来
- 4、域名传输时,取决于域名原始用户的邮箱是否有效,以及他是否将密码发送到此邮箱。
- 4.域名的整体品牌营销力
- 以上的就是为大家介绍的关于域名的详解域名注册:域名注册0
- 这个不用多说,不同平台的注册价格不同,且不同平台对域名释放交易的把控与曝光不同,当然价格相对便宜且平台渠道广操作便利的平台最好。
- 3、商标域名一经注册,就可以作为域名裁决过程中的主要信息之一。这可以大大增加公司被抢注的相关域名胜诉的机会。