在《Python的编码与解码初探》中,对Python的编码知识稍做了一点研究,本文再深入一些,将字符编码以及不同进制的关系梳理一下。
1. ASCII和Unicode编码
先来看看ASCII和Unicode编码的一个例子:
- 字母
A
用ASCII编码是十进制的65
,二进制的01000001
; - 字符
0
用ASCII编码是十进制的48
,二进制的00110000
,注意字符'0'
和整数0
是不同的; - 汉字
中
已经超出了ASCII编码的范围,用Unicode编码是十进制的20013
,二进制的01001110 00101101
。
Python使用chr()
内置函数创建单字符Unicode字符串,该函数采用整数,并返回长度为1的包含相应码位的Unicode字符串。反向操作是内置ord()
函数,它使用一个字符的Unicode字符串并返回码位值。
>>> ord('A')
65
>>> ord('0')
48
>>> ord('中')
20013
>>> chr(65)
'A'
>>> chr(48)
'0'
>>> chr(20013)
'中'
ASCII编码是1个字节,而Unicode编码通常是2个字节,如果把ASCII编码的A
用Unicode编码,只需要在前面补0
就可以,因此,A
的Unicode编码是00000000 01000001
。
Unicode编码比ASCII编码需要多一倍的存储空间,于是又有了“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
字符 | ASCII | Unicode | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
中 | - | 01001110 00101101 | 11100100 10111000 10101101 |
2. 二进制、八进制、十进制和十六进制之间的转换
上面可以看到,ord()
函数获取的是字符的整数表示,为十进制。
当我们把以Unicode表示的str
编码为bytes
类型时,其输出是十六进制:
>>> '中'.encode('utf-8')
b'\xe4\xb8\xad'
而我们最终需要的是二进制的表达方式,于是这里就需要在不同的进制之间进行转换。
2.1 转十进制
Python使用内置的 int(x, base=10) 函数来进行十进制的转换,第一个参数是数字或字符串,第二个参数是说明该字符串是几进制的数,默认为十进制。
>>> int('0xe4', 16)
228
>>> int('01000001', 2)
65
>>> int('17', 8)
15
2.2 转十六进制
Python使用内置的 hex(x) 函数将十进制的整数转换为以小写0x
开头的十六进制字符串。如需要转换二进制或八进制,需要先将其转换为十进制。
>>> hex(228)
'0xe4'
>>> hex(int('01000001', 2))
'0x41'
也可以通过以下方式来格式化十六进制的显示:
>>> '%#x' % 228, '%x' % 228, '%X' % 228
('0xe4', 'e4', 'E4')
>>> format(228, '#x'), format(228, 'x'), format(228, 'X')
>>> f'{228:#x}', f'{228:x}', f'{228:X}'
2.3 转二进制
Python使用内置的 bin(x) 函数将十进制的整数转换为以小写0b
开头的二进制字符串。如需要转换八进制或十六进制,需要先将其转换为十进制。
>>> bin(3)
'0b11'
>>> format(3, '#b'), format(3, 'b')
('0b11', '11')
>>> f'{3:#b}', f'{3:b}'
2.4 转八进制
Python使用内置的 oct(x) 函数将十进制的整数转换为以小写0o
开头的八进制字符串。如需要转换二进制或十六进制,需要先将其转换为十进制。
>>> oct(8)
'0o10'
>>> '%#o'% 8 , '%o' % 8
('0o10', '10')
>>> format(8, '#o'), format(8, 'o')
('0o10', '10')
>>> f'{8:#o}', f'{8:o}'
3. 二进制填充
当使用二进制来表示字符编码时,我们一般是用填充了0的八位数字(1个字节)来表示。这里可以使用Python的 f-string 来进行格式化:
>>> f"{3:08b}"
'00000011'
其中3是需要转换的数,08是以0来填充满8位,b是要转换为二进制。
对字符串,可以使用下面的方式来进行格式化:
>>> def make_bitseq(s: str) -> str:
... if not s.isascii():
... raise ValueError("ASCII only allowed")
... return ' '.join(f"{ord(i):08b}" for i in s)
>>> make_bitseq("puran")
'01110000 01110101 01110010 01100001 01101110'
4. 输出Unicode字符
通过Unicode列表可知,标准的Unicode编码是U+0417
这样的格式,在Python中,是需要改写成\u0417
才能正确的识别并输出对应的字符。
>>> '\u0417'
'З'
>>> 'U+0417'
'U+0417'
5. Python中的emoji
在Unicode中,还包含emoji的编码,当直接输出其编码时,一般得不到我们想要的内容:
>>> '\u1F600'
'ὠ0'
在Python中使用emoji有两种方式:
- 通过emoji的Unicode编码来输出
- 借助emoji库并通过emoji的别名来输出
1. 通过emoji的Unicode编码
在通过emoji的Unicode编码来输出emoji时,需要对编码进行改造,将编码扩展为8位。比如,👍的Unicode编码为U+1F44D
,在输出时需将其改造为\U0001F44D
:
>>> print('\U0001F44D')
👍
2. 借助emoji库:
emoji 库的安装很简单:pip install emoji --upgrade
>>> import emoji
>>> print(emoji.emojize("Python is :thumbs_up:"))
Python is 👍
>>> print(emoji.demojize("Python is 👍"))
Python is :thumbs_up:
这个列表列出了几乎所有的emoji及其名字:https://www.webfx.com/tools/emoji-cheat-sheet/,在使用时直接参考即可。