我最近注意到,Python在使用utf-8-sig
编码附加到文件时的行为非常不明显。见下文:
>>> import codecs, os
>>> os.path.isfile('123')
False
>>> codecs.open('123', 'a', encoding='utf-8-sig').write('123n')
>>> codecs.open('123', 'a', encoding='utf-8-sig').write('123n')
以下文本以文件结尾:
<BOM>123
<BOM>123
那不是虫子吗?这是不合逻辑的。有人能向我解释为什么这么做吗?为什么他们不设法只在文件不存在并且需要创建时才准备BOM?
不,这不是一个bug;这完全是正常的、意料之中的行为。编解码器无法检测已写入文件的内容;例如,您可以使用它来附加到预先创建但为空的文件。该文件不是新文件,但也不会包含BOM表。
还有其他用例,其中编解码器用于流或字节串(例如,不与codecs.open()
一起使用),其中根本没有要测试的文件,或者开发人员希望始终在输出开始时强制执行BOM。
仅在新文件上使用utf-8-sig
;无论何时使用,编解码器都会总是写出BOM。
如果你直接使用文件,你可以自己测试启动;使用utf-8
,手动编写BOM,这只是一个编码的U+FEFF ZERO WIDTH NO-BREAK SPACE:
import io
with io.open(filename, 'a', encoding='utf8') as outfh:
if outfh.tell() == 0:
# start of file
outfh.write(u'ufeff')
我使用了较新的io.open()
而不是codecs.open()
;io
是为Python3开发的新I/O框架,根据我的经验,在处理编码文件方面比codecs
更健壮。
请注意,UTF-8 BOM几乎毫无用处。UTF-8没有可变字节顺序,因此只有一个字节顺序标记。另一方面,UTF-16或UTF-32可以用两个不同字节顺序中的一个来编写,这就是为什么需要BOM的原因。
UTF-8 BOM主要由Microsoft产品用于自动检测文件的编码(例如而不是其中一个遗留代码页)。