在Python中将没有BOM的UTF-8转换为带有BOM的UTF-8

发布于 2021-01-29 14:55:34

这里有两个问题。我有一组通常是带有BOM的UTF-8文件。我想将它们(理想情况下)转换为没有BOM的UTF-8。似乎codecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)可以解决这个问题。但是我真的看不到任何有关用法的好例子。这将是处理此问题的最佳方法吗?

source files:
Tue Jan 17$ file brh-m-157.json 
brh-m-157.json: UTF-8 Unicode (with BOM) text

同样,如果我们能够处理清楚知道的不同输入编码(看到的ASCII和UTF-16),那将是理想的。看来这一切都是可行的。是否有一种解决方案可以采用任何已知的Python编码并以UTF-8格式输出而无需BOM?

*从下面 *编辑1个 建议的sol’n(谢谢!)

fp = open('brh-m-157.json','rw')
s = fp.read()
u = s.decode('utf-8-sig')
s = u.encode('utf-8')
print fp.encoding  
fp.write(s)

这给了我以下错误:

IOError: [Errno 9] Bad file descriptor

新闻快讯

有人在评论中告诉我,错误是我以’rw’模式而不是’r +’/’r + b’模式打开文件,因此我最终应重新编辑问题并删除已解决的部分。

关注者
0
被浏览
148
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    只需使用“ utf-8-sig”编解码器

    fp = open("file.txt")
    s = fp.read()
    u = s.decode("utf-8-sig")
    

    这样就可以得到一个unicode没有BOM的字符串。然后,您可以使用

    s = u.encode("utf-8")
    

    以在中获取普通UTF-8编码的字符串s。如果文件很大,则应避免将它们全部读入内存。BOM只是文件开头的三个字节,因此您可以使用以下代码将其从文件中删除:

    import os, sys, codecs
    
    BUFSIZE = 4096
    BOMLEN = len(codecs.BOM_UTF8)
    
    path = sys.argv[1]
    with open(path, "r+b") as fp:
        chunk = fp.read(BUFSIZE)
        if chunk.startswith(codecs.BOM_UTF8):
            i = 0
            chunk = chunk[BOMLEN:]
            while chunk:
                fp.seek(i)
                fp.write(chunk)
                i += len(chunk)
                fp.seek(BOMLEN, os.SEEK_CUR)
                chunk = fp.read(BUFSIZE)
            fp.seek(-BOMLEN, os.SEEK_CUR)
            fp.truncate()
    

    它打开文件,读取一个块,并将其比读取它的位置早3个字节写入文件。该文件被原位重写。较简单的解决方案是将较短的文件写入新文件,例如newtover的answer。那会更简单,但是在短时间内使用两倍的磁盘空间。

    至于猜测编码,则可以从最具体到最不具体遍历整个编码:

    def decode(s):
        for encoding in "utf-8-sig", "utf-16":
            try:
                return s.decode(encoding)
            except UnicodeDecodeError:
                continue
        return s.decode("latin-1") # will always work
    

    UTF-16编码的文件不会解码为UTF-8,因此我们首先尝试使用UTF-8。如果失败,那么我们尝试使用UTF-16。最后,我们使用Latin-1-这将始终有效,因为所有256个字节在Latin-1中都是合法值。None在这种情况下,您可能想要返回,因为这实际上是一个后备,并且您的代码可能想要更仔细地处理(如果可以)。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看