了解字节,字符串以及unicode# 了解字节,字符串以及unicode之间的区别
在Python3
中,有两种类型的字符代表序列:bytes(字节)
和 str(字符串)
。字节的实例包含8个原生的比特值,而字符串的实例则是用Unicode字符来堆砌的。
在Python2
中,有两种类型的字符代表序列:str(字符串)
和 unicode(Unicode字符)
。与Python3
相反,字符串实例代表着原生的8比特值序列,而unicode
则由Unicode
字符堆砌而成。
有许多方法来表示Unicode字符的二进制数据(原生的8比特值 序列)。最常用的编码方式为UTF-8
编码。还有一点很重要,那就是Python3
中的字符串实例和Python2
中的unicode
实例并没有相关联的二进制编码。所以要想将Unicode
字符转换成二进制数据,就必须使用encode
方法,反过来,要想把二进制数据转换成Unicode
字符,就必须使用decode
方法。
当你开始写Python
程序的时候,在接口的最开始位置声明对Unicode
的编码解码的细节很重要。在你的代码中,最核心的部分应使用Unicode
字符类型(Python3
中使用str
,Python2
中使用unicode
)并且不应该考虑关于字符编码的任何其他方式。本文允许你使用自己喜欢的可替代性的文本编码方式(如Latin-1
,Shift JIS
, Big5
),但是应该对你的文本输出编码严格的限定一下(理想的方式是使用UTF-8
编码)。
由于字符类型的不同,导致了Python代码中出现了两种常见的情形的发生。
- 你想操作
UTF-8
(或者其他的编码方式)编码的8比特值 序列。 - 你想操作没有特定编码的
Unicode
字符。 所以你通常会需要两个工具函数来对这两种情况的字符进行转换,以此来确保输入值符合代码所预期的字符类型。
在Python3
中
- 你将需要一个方法,接收
str
或者bytes
,总是来返回str
类型的数据。如下:
def to_str(bytes_or_str):
if isinstance(bytes_or_str,bytes):
value = bytes_or_str.encode('utf-8')
else:
value = bytes_or_str
# str类型的数据
return value
- 同理,我们需要另一个方法,来接收
str
或bytes
,总是来返回bytes
类型的数据。
def to_bytes(bytes_or_str):
if isinstance(bytes_or_str,str):
value = bytes_or_str.encode('utf-8')
else:
value = bytes_or_str
# 字节类型的数据
return value
在Python2
中
- 需要一个方法,来接收str或者unicode类型的数据,总是来返回unicode类型的数据。
def to_unicode(unicode_or_str):
if isinstance(unicode_or_str,str):
value = unicode_or_str.encode('utf-8')
else:
value = unicode_or_str
# unicode类型的数据
return value
- 同理,需要一个接收str或者unicode类型的数据,总是来返回str类型的数据。
def to_str(unicode_or_str):
if isinstance(unicode_or_str,unicode):
value = unicode_or_str.encode('utf-8')
else:
value = unicode_or_str
# str类型的数据
return value
两大陷阱
在Python
中处理原生的8比特值 序列以及Unicode
字符的时候,有两大陷阱。
一个是在Python2
中,当一个str
数据仅仅包含7比特的ASCII
码字符的时候,unicode
和str
实例看起来是一致的。
- 可以使用‘+’运算符和合并str和unicode。
- 可以使用等价或者不等价运算符来比较str和unicode实例。
- 可以使用unicode来代换 像‘%s’这种字符串中的格式化占位符。
以上行为意味着,如果你的代码中仅仅处理原生的7比特序列,那么便可以不必在意是str
类型的数据还是unicode
类型的数据了。在Python3
中,bytes
和str
实例是不可能等价的,即使是空的字符串也不可能等价。所以你必须谨慎地对正在处理的代码进行字符类别的区分处理。
另一个是在Python3
中,涉及到文件处理的操作(使用内置的open
函数)会默认的以UTF-8
进行编码。而在Python2
中默认采用二进制形式来编码。这也是导致意外事故发生的根源,特别是对于那些更习惯于使用Python2
的程序员而言。
比方说,你想将几个随机的二进制数据写入到一个文件中。在Python2
中,下面的这段代码可以正常的工作,但是在Python3
中却会报错并退出。详细信息如下。
def open('/tmp/random.bin','w') as f:
f.write(os.urandom(10))
>>>
TypeError: must be str,not bytes
导致这个异常发生的原因是在Python3
中对于open
函数又新增了一个名为encoding
的参数。此参数默认为UTF-8
。这使得其对于文件的读写操作预期的源为包含了Unicode
字符串的str
实例,而不是包含了二进制数据的字节文件。
为了使得上面的函数正常的工作,我们必须指明被操作的数据是以‘wb’模式打开,而不是简单的‘w’模式。这里,作者介绍了一个在Python2
和Python3
中都通用的方法,详细如下。
with open('/tmp/random.bin','wb) as f:
f.write(os.urandom(10))
好了,写文件的问题算是解决了,但是不要忘了还有读文件的问题哦。同样的我们也只需要改变一下读文件的模式即可。即‘r’换成‘rb’。
备忘录:
- 在
Python3
中,字节包含的是8个比特值的序列,str
是包含Unicode
的字符的串。字节和字符串实例不能同时出现在操作符‘>’ 或者 ‘+’中。 - 在
Python2
中,str
是包含8个比特值的序列,unicode
是包含Unicode
字符的串,二者可以同时出现在只包含7个比特的ASCII
码的运算中。 - 使用工具函数来确保程序输入的数据时程序预期的类型。
- 总是使用‘wb’和‘rb’模式来写文件和读文件。