Python 在文件末尾附加时,UTF-8-SIG BOM 位于文件中间
我最近注意到,使用utf-8-sig
编码往文件添加内容时,Python的表现有点让人摸不着头脑。看看下面的例子:
>>> import codecs, os
>>> os.path.isfile('123')
False
>>> codecs.open('123', 'a', encoding='utf-8-sig').write('123\n')
>>> codecs.open('123', 'a', encoding='utf-8-sig').write('123\n')
最终写入文件的内容是:
<BOM>123
<BOM>123
这难道不是个bug吗?这实在是太不合逻辑了。 有没有人能解释一下为什么会这样? 为什么他们不在文件不存在需要创建的时候才加上BOM呢?
1 个回答
10
不,这不是个错误;这是完全正常的行为。这个编码器无法知道文件里已经写了多少内容;比如你可以用它来往一个已经创建但空的文件里追加内容。这个文件虽然不是新的,但也不会包含字节顺序标记(BOM)。
还有一些其他的情况,比如在处理流或字节串时(例如,不使用codecs.open()
),根本就没有文件可以测试,或者开发者希望在输出的开头总是强制添加一个BOM。
只在新文件上使用utf-8-sig
;每次使用这个编码器时,它总是会写出BOM。
如果你直接处理文件,可以自己检查开头;可以使用utf-8
,然后手动写入BOM,这实际上就是一个编码后的U+FEFF 零宽无断空格:
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
是为Python 3开发的新输入输出框架,在处理编码文件时比codecs
更强大,至少在我看来是这样的。
需要注意的是,UTF-8的BOM实际上几乎没什么用。UTF-8没有可变的字节顺序,所以只有一个字节顺序标记。而UTF-16或UTF-32可以用两种不同的字节顺序来写,这就是为什么需要BOM的原因。
UTF-8的BOM主要是被微软的产品用来自动检测文件的编码(例如不是那些旧的代码页)。