Python3 中的 str 和 bytes
与 Python2.X 不同,Python3.X 严格区分了 str 和 bytes 两种类型。文本为 Unicode,由 str 类型表示;二进制数据则由 bytes 表示。
Python3.X 不会以任意隐式的方式混用 str 和 bytes。因此使用者不能拼接字符串和字节包,也无法在字节包里搜索字符串(反之亦然),也不能将字符串传入参数为字节包的函数(反之亦然)。
例如 Python3.X 中的socket.send()
函数,如果传入未编码的字符串,会报错:
>>> client.send("test str")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'
>>> client.send(b"test str") #将参数转换成 bytes 类型
8 #返回发送的数据长度
编码发展的历史
在计算机历史的早期,美国为代表的英语系国家主导了整个计算机行业,26个英文字母组成了多样的英语单词、语句、文章。因此,最早的字符编码规范是ASCII码,一种8位(即1个字节)的编码规范,它可以涵盖整个英语系的编码需要。
编码是什么?编码就是把一个字符用一个二进制来表示。我们都知道,所有的东西,不管是英文、中文还是符号等等,最终存储在磁盘上都是01010101这类东西。在计算机内部,读取和存储数据归根结底,处理的都是0和1组成的比特流。问题来了,人类看不懂这些比特流,如何让这些010101对人类变得可读呢?于是出现了字符编码,它是个翻译机,在计算机内部某个地方,偷偷帮我们将比特流翻译成人类可以直接理解的文字。对于一般用户,不需要知道这个过程是什么原理,是怎么执行的。但是对于程序员却是个必须搞清楚的问题。
以ASCII
编码为例,它规定1个字节8个比特位代表1个字符的编码,逐个字节进行解读。例如:01000001表示大写字母A,有时我们会用65这个十进制来表示A在ASCII
中的编码。8个比特位,可以无重复地最多表示2的8次方个字符;但标准的ASCII
码 只有7位,码值范围是0-127,最高位为0。
后来,计算机得到普及,中文、日文、韩文等等国家的文字需要在计算机内表示,像ASCII
这种单字节编码已经远远不够了,于是标准组织制定出了UNICODE(万国码),它规定任何一个字符(不管哪国的)至少以两个字节表示。其中,英文字母就是用2个字节,而汉字是3个字节。这个编码虽然很好,满足了所有人的要求,但是它不兼容ASCII
,同时还占用较多的空间和内存。而在计算机世界更多的字符是英文字母,明明可以1个字节就能够表示,非要用2个,就造成了空间资源的浪费了。
于是UTF-8
编码应运而生,它规定英文字母系列用1个字节表示,汉字用3个字节表示等等。因此,它兼容ASCII
,可以解码早期的文档。UTF-8
很快就得到了广泛的应用。
在编码的发展历程中,我国还创造了自己的编码方式,例如GBK
,GB2312
,BIG5
。他们只局限于在国内使用,不被国外认可。在GBK
编码中,中文汉字占2个字节。
bytes 和 str 之间的转换
test0 = 'abc'
test1 = b'abc'
print(type(test0), test0) --> <class 'str'> 'abc'
print(type(test1), test1) --> <class 'bytes'> b'abc'
test2 = bytes(test0, 'utf-8')
test3 = str(test1, 'utf-8')
test4 = str(test1)
print(type(test2), test2) --> <class 'bytes'> b'abc'
print(type(test3), test3) --> <class 'str'> 'abc'
print(type(test4), test4) --> <class 'str'> "b'abc'"
test5 = test0.encode() #参数可输入编码格式,默认utf-8
test6 = test1.decode()
print(type(test5), test5) --> <class 'bytes'> b'abc'
print(type(test6), test6) --> <class 'str'> 'abc'