python里的bytes类型理解 - aierlma/git_instructions GitHub Wiki
在python中,Unicode码和字符可以认为是相同的。
考虑下面的例子
print("\u0049") # I
print("I") # I
是相同的结果,再考虑一个例子
print("\u7231") # 爱
print("爱") # 爱
这就是我说的,"相同"的意思。至于如何获得一个字节的Unicode码,只需要ord("I")
,可以得到一个十进制的数字,转成16进制去掉前面的0x加上U+ 或者 \u 就是Unicode码
而字符与bytes类型具有一一对应
Unicode与utf-8的转换
UCS-4(UNICODE)编码 | UTF-8字节流 |
---|---|
U-00000000 – U-0000007F | 0xxxxxxx |
U-00000080 – U-000007FF | 110xxxxx 10xxxxxx |
U-00000800 – U-0000FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U-00010000 – U-001FFFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
U-00200000 – U-03FFFFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
U-04000000 – U-7FFFFFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
Unicode编码0x20C30在0x010000-0x10FFFF之间,使用4字节模板:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
因此通过utf-8 encoding 可以将Unicode码变为utf-8的码。 事实上,许多底层硬件或软件在应用时并不使用Unicode码,而是使用utf-8码,utf-8码由16进制的字符组成,两个字符为1字节,由类似uft-8码形成的字符串就是bytes类型。
事实上bytes类型对于编码方式不关心,无论是utf-8还是ASCII还是其他的,它只是一堆二进制数罢了。
你可以用特定编码方式把字符变成一堆二进制数(准确来说是十六进制),考虑下面的例子
print(bytes("爱",encoding = "gbk")) # b'\xb0\xae'
print(bytes("爱",encoding = "utf-8")) # b'\xe7\x88\xb1'
print(bytes("\u7231",encoding = "gbk")) # b'\xb0\xae'
print(bytes("\u7231",encoding = "utf-8")) # b'\xe7\x88\xb1'
可以看到,字符与Unicode码确实是等价的,通过特定的编码方式可以把他们变成字节类型
python的字节类型有一个特点,当被编码的字符不是ASCII码或其衍生码时,打印结果会是真实储存结果(二进制)的十六进制表示,考虑上面的例子
print(bytes("汉",encoding = "utf-8")) # b'\xe6\xb1\x89'
其真实储存为11100110 10110001 10001001
,python的显示为b'\xe6\xb1\x89'
当编码的字符恰好是ASCII码或其衍生码时,打印结果是自身(前面加个b),考虑下面的例子
print(bytes("I",encoding = "utf-8")) # b'I'
print(b'\x49') # b'I'
从以上的示例我们看出这样的关系
'\u0049' <==> 'I' <=utf-8 encode=> b'I' <==> b'\x49' <==> 0100 1001
这就是这篇文章的关键,请确保你看懂了上述关系,其中utf-8 encode 表示一个映射关系,自然也可以有逆映射,即decode
是否有其他创建字符类型的方法呢?有。对于ASCII码及其衍生码来说,只需要在前面加一个b。
hello = b'hello bytes'
print(hello) # b'hello bytes'
注意到,这里的空格也被编码了,空格的utf-8编码为\x20
bytes()
也可以为小于256的数字编码成单个字节,这是因为一个字节只能表示2^8种字符。如果你想让它编码多个字节,那么请提供多个小于256的数字,而不是输入一个大于等于256的数字,考虑下面的例子
print(bytes([88,89])) # b'XY'
print(bytes(f"{chr(88)+chr(89)}",encoding = "utf-8")) # b'XY'
print(bytes([35,255])) # b'#\xff'
print(bytes([256])) # ValueError: bytes must be in range(0, 256)
其中chr(x)可以把一个数字转换成对应的字符,这种对应发成在Unicode码与字符之间,ord是chr的逆映射。考虑下面的例子
print("我") # 我
print(chr(25105)) # 我
print("\u6211") # 我
print(hex(25105),6211) # 0x6211 6211
其中hex(x)表示把10进制的x换成16进制,忽略前面的0x,可以看到25105就是16进制的6211