Просто используйте кодек "utf-8-sig" :
fp = open("file.txt")
s = fp.read()
u = s.decode("utf-8-sig")
Это дает вам строку unicode
без спецификации.Затем вы можете использовать
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's 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
в этом случае, поскольку это действительно запасной вариант, и ваш код может захотеть обработать это более осторожно (если это возможно).