如何在Python中设置脚本自动检测纯文本文件的字符编码?

3 投票
3 回答
1898 浏览
提问于 2025-04-15 23:55

我写了一个脚本,基本上是对一个纯文本文件进行大规模的查找和替换。

目前这个脚本在处理ASCII、UTF-8和UTF-16编码的文件时都能正常工作(可能还有其他编码,但我只测试了这三种),只要在脚本里指定了编码(下面的示例代码指定的是UTF-16)。

有没有办法让这个脚本自动识别输入文件使用的是哪种字符编码,并且自动把输出文件的字符编码设置成和输入文件一样呢?

findreplace = [
('term1', 'term2'),
]    

inF = open(infile,'rb')
    s=unicode(inF.read(),'utf-16')
    inF.close()

    for couple in findreplace:
        outtext=s.replace(couple[0],couple[1])
        s=outtext

    outF = open(outFile,'wb')
    outF.write(outtext.encode('utf-16'))
    outF.close()

谢谢!

3 个回答

1

不,没有这种方法。你必须把这些信息直接写进文件里,或者从外部来源获取。

有一些启发式方法,可以通过分析文件中字节出现的频率来猜测文件的编码方式;不过我不会把这些方法用在任何重要的数据上。

3

一些观察和问题:

(1) ASCII是UTF-8的一部分,这意味着如果一个文件可以用ASCII成功解码,那么它也可以用UTF-8成功解码。所以你可以把ASCII从你的考虑中去掉。

(2) 在查找替换中,两个术语会包括非ASCII字符吗?如果答案是“是”,那么这就意味着想要用和输入文件相同的字符集来写输出文件可能会很难,甚至不可能。

(3) 为什么不把所有输出文件都用同一种能处理所有Unicode字符的编码,比如UTF-8呢?

(4) UTF-8文件有BOM吗?

(5) 你还期望需要处理哪些其他字符集呢?

(6) 你所称的UTF-16是哪四种可能性中的一种(UTF-16LE / UTF-16BE)x(有BOM / 没有BOM)?注意,我故意不想从你代码中的'utf-16'推测任何东西。

(7) 请注意,chardet在没有BOM的情况下无法检测到UTF-16xE。chardet在处理非-*x和旧字符集时还有其他盲点。

更新 这里有一些代码片段,你可以用来确定什么是“ANSI”,并尝试使用有限的编码列表进行解码。注意:这假设是在Windows环境下。

# determine "ANSI"
import locale
ansi = locale.getdefaultlocale()[1] # produces 'cp1252' on my Windows box.

f = open("input_file_path", "rb")
data = f.read()
f.close()

if data.startswith("\xEF\xBB\xBF"): # UTF-8 "BOM"
    encodings = ["utf-8-sig"]
elif data.startswith(("\xFF\xFE", "\xFE\xFF")): # UTF-16 BOMs
    encodings = ["utf16"]
else:
    encodings = ["utf8", ansi, "utf-16le"]
# ascii is a subset of both "ANSI" and "UTF-8", so you don't need it.
# ISO-8859-1 aka latin1 defines all 256 bytes as valid codepoints; so it will
# decode ANYTHING; so if you feel that you must include it, put it LAST.
# It is possible that a utf-16le file may be decoded without exception
# by the "ansi" codec, and vice versa.
# Checking that your input text makes sense, always a very good idea, is very 
# important when you are guessing encodings.

for enc in encodings:
    try:
        udata = data.decode(enc)
        break
    except UnicodeDecodeError:
        pass
else:
    raise Exception("unknown encoding")

# udata is your file contents as a unicode object
# When writing the output file, use 'utf8-sig' as the encoding if you
# want a BOM at the start. 
4

根据J.F. Sebastian分享的链接,可以试试chardet这个工具。

需要注意的是,通常情况下,我们无法100%准确地检测每一个输入文件的字符编码。换句话说,有些文件可能用不同的编码方式都能被理解,而我们可能无法判断到底用的是哪种编码。chardet使用了一些经验方法,并给出一个置信度,表示它对所识别的字符编码有多“确定”。

撰写回答