Python 在文件末尾附加时,UTF-8-SIG BOM 位于文件中间

8 投票
1 回答
13264 浏览
提问于 2025-04-18 03:16

我最近注意到,使用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主要是被微软的产品用来自动检测文件的编码(例如不是那些旧的代码页)。

撰写回答