(文章写的有点乱,凑合看吧)

在Python 3中有两种表示字符串的方式:bytes和str。bytes的实例包含原始的8位值(raw 8-bit values);str实例包含Unicode字符。

Python 2中也有两种表示字符串的方式:str和unicode。与Python 3对比,Python 2中str实例包含原始的8位值,unicode包含Unicode字符。

有很多种方式能够将Unicode字符表示为二进制数据(binary data,raw 8-bit values)。最常用的一种编码格式是UTF-8。需要注意的是,Python 3中的str实例以及Python 2中的unicode实例都没有相关的二进制编码格式。要想使Unicode字符转换成二进制数据必须使用encode方法。要想使二进制数据转换成Unicode字符必须使用decode方法。




python 文件 unicode转utf8 python unicode转str_unicode字符


Python 3代码示例:


python 文件 unicode转utf8 python unicode转str_Python_02


当你在编写Python程序时,最好在远离接口的地方进行decode、encode操作。代码的核心应该使用Unicode字符类型(Python 3中使用str,Python 2中使用unicode),并且不应当对任何字符串进行encode操作。这样做的好处就是,当你需要严格控制你的输出文本的编码格式时,你的代码可以很容易的转换成其它字符编码格式,例如latin-1、Shift JIS、 Big5等。Python针对字符类型的分类引出了两种问题:

  • 按照8位原始值的形式操作UTF-8字符串或者其它编码格式字符串;
  • 操作Unicode字符。

此时你需要一些帮助方法来帮助你修改方法的输入参数,使这些参数的编码格式符合你的要求。

在Python 3中你需要一个方法接收str或bytes,返回str:


python 文件 unicode转utf8 python unicode转str_ico_03


同时需要另一个方法接收str或者bytes返回bytes:


python 文件 unicode转utf8 python unicode转str_Python_04


在Python 2中你需要一个方法,这个方法接收str或unicode,返回unicode:

def to_unicode(unicode_or_str): if isinstance(unicode_or_str, str): value = unicode_or_str.decode('utf-8') else: value = unicode_or_str return value # Instance of unicode

同时需要另一个方法用于接收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 return value # Instance of str

在Python中处理原始8位值和Unicode字符时有两个大问题。

第一个问题是在Python 2中,如果一个字符串只包含7位的ACSII字符时,str和unicode实例看起来是一样的。

  • 可以使用+链接两个str和unicode对象;
  • 可以使用等于、不等于操作符比较两个str和unicode对象;
  • 可以使用unicode实例作为格式化字符串,例如"%s"。

上面的行为意味着不论一个方法期望接收的是str还是unicode对象,你都可以随意传递这两个中的一个。在Python 3中,bytes和str从来都不是一回事,甚至空对象也不一样,所以在传递字符序列时一定要注意所传递的数据类型。

另一个问题是,在Python 3中文件处理方法返回的对象(open方法返回的对象)默认是使用UTF-8编码的。在Python 2中文件操作默认返回二进制编码(binary encoding)。这会导致一些奇怪的问题,尤其是那些习惯于Python 2的程序员。

例如,想要向文件中写入一些随机的二进制数据,Python 2就可以实现,但是Python 3就会出现异常。

with open('/tmp/random.bin', 'w') as f: f.write(os.urandom(10))>>>TypeError: must be str, not bytes

出现这个错误的原因是因为在Python 3中open方法增加了一个encoding参数。这个参数的默认值是UTF-8,这使得open方法返回对象的read和write方法都是使用包含unicode字符的str对象而不是使用bytes数据。

要想使read、write工作正常,你必须指定文件是按照二进制写(wb)模式打开的,而不是简单的使用写(w)模式。修改上面代码,使用"wb"默认就可以同时在Python 2和Python 3中正常执行了:

with open('/tmp/random.bin', 'wb') as f: f.write(os.urandom(10))

同样的问题也会出现在read方法中,解决方法是一样的,修改文件打开模式,使用"rb"。

总结:

  • 在Python 3中,bytes包含8位序列值,str包含unicode字符序列。 bytes和str实例不能一起使用(比如, > 或 +);
  • 在Python 2中,str包含8位序列值,unicode包含unicode字符序列。当str仅包含7位ACSII字符时,str和unicode实例可以一起使用;
  • 使用heler方法来保证输入的字符串是你希望的类型 (如:8位值序列、UTF-8字符串、Unicode字符串等);
  • 如果想从文件读或者写二进制数据,一定要使用二进制形式打开文件(“rb”或者"wb")。