Python3的文件编码
Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用 ASCII。
由于Python对源码的编码,Python 2 下如果加载的 .py 模块中包含 UTF-8 之外的数据,而且没有声明编码(比如中文),会得到类似下面的消息:
SyntaxError: Non-UTF-8 code starting with '\xe1' in file...
解决方法是在文件头添加utf-8的注释说明,如:
# -*- coding: utf-8 -*-
某些编码(如 ASCII 和多字节的 GB2312)不能表示所有 Unicode 字符。然而,UTF 编码的设计目的就是处理每一个 Unicode 码位。‘utf_?’ 编码能处理任何字符串。utf-8是目前 Web 中最常见的 8 位编码; 与 ASCII 兼容(纯 ASCII 文本是有效的 UTF-8 文本)。
Python 3 的源码不再限于使用 ASCII,而是默认使用优秀的 UTF-8 编码,因此要修正源码的陈旧编码(如 ‘cp1252’)问题,最好将其转换成 UTF-8,别去写 coding 注释。如果你用的编辑器不支持 UTF-8,那么是时候换一个了。
有些人不喜欢这么做。支持始终使用 ASCII 标识符的人认为,这样便于所有人阅读和编辑代码。这些人没切中要害:源码应该便于目标群体阅读和编辑,而不是“所有人”。如果代码属于跨国公司,或者是开源的,想让来自世界各地的人作贡献,那么标识符应该使用英语,也就是说只能使用 ASCII 字符。
但是,如果你是巴西的一位老师,那么使用葡萄牙语正确拼写变量和函数名更便于学生阅读代码。而且,这些学生在本地化的键盘中不难打出变音符号和重音元音字母。这是我作为说葡萄牙语的巴西人的观点,不过我相信也适用于其他国家和文化:选择对团队而言易于阅读的人类语言,然后使用正确的字符拼写。
(很认同Python3的这个改变,简单、灵活又实用,才是Pythonic!)
找出文件编码可以使用chardet工具。
二进制序列编码文本通常不会明确指明自己的编码,但是 UTF 格式可以在文本内容的开头添加一个字节序标记。
文件的BOM: UTF-16 编码的序列开头有几个额外 的字节,如下所示:
>>> u16 = 'El Niño'.encode('utf_16')
>>> u16
b'\xff\xfeE\x00l\x00\x00N\x00i\x00\xf1\x00o\x00'
指的是 b’\xff\xfe’。这是BOM,即字节序标记(byte-order mark),指明编码时使用 Intel CPU 的小字节序。
在小字节序设备中,各个码位的最低有效字节在前面;在大字节序 CPU 中,编码顺序是相反的。
为了避免混淆,UTF-16 编码在要编码的文本前面加上特殊的不可见字符 ZERO WIDTH NO-BREAK SPACE(U+FEFF)。在小字节序系统中,这个字符编码为 b’\xff\xfe’(十进制数 255, 254)。
UTF-16 有两个变种:UTF-16LE,显式指明使用小字节序;UTF-16BE,显式指明使用大字节序。如果使用这两个变种,不会生成 BOM。
如果有 BOM,UTF-16 编解码器会将其过滤掉,为你提供没有前导 ZERO WIDTH NO-BREAK SPACE 字符的真正文本。
UTF-8 的一大优势是,不管设备使用哪种字节序,生成的字节序列始终一致,因此不需要 BOM。(8位一个字节,不受大小字节序的影响)
尽管如此,某些 Windows 应用(尤其是 Notepad)依然会在 UTF-8 编码的文件中添加 BOM;而且,Excel 会根据有没有 BOM 确定文件是不是 UTF-8 编码,否则,它假设内容使用 Windows 代码页(codepage)编码。UTF-8 编码的 U+FEFF 字符是一个三字节序列:b’\xef\xbb\xbf’。因此,如果文件以这三个字节开头,有可能是带有 BOM 的 UTF-8 文件。然而,Python 不会因为文件以 b’\xef\xbb\xbf’ 开头就自动假定它是 UTF-8 编码的。
参考《流畅的Python》第4章关于文件解码的内容。