Преобразование UTF-8 с спецификацией в UTF-8 без спецификации в Python
Два вопроса здесь. У меня есть набор файлов, которые обычно UTF-8 с спецификацией. Я хотел бы преобразовать их (в идеале на месте) в 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 без спецификации?
изменить 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
Newsflash
В комментариях мне говорят, что ошибка в том, что я открываю файл с режимом "rw" вместо "r +" / "r + b", поэтому я должен в конечном итоге отредактировать свой вопрос и удалить решенную часть.
7 ответов
Просто используйте кодек "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 байта раньше, чем там, где он его прочитал. Файл переписан на месте. Как более простое решение - записать более короткий файл в новый файл, например , ответ на новый запрос. Это было бы проще, но за короткий промежуток времени удвоить дисковое пространство.
Что касается угадывания кодировки, то вы можете просто перебрать кодировку от самой к наименее определенной:
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
вместо этого в данном случае, поскольку это действительно запасной вариант, и ваш код может захотеть обработать это более осторожно (если это возможно).
В Python 3 это довольно просто: прочитать файл и переписать его utf-8
кодирование:
s = open(bom_file, mode='r', encoding='utf-8-sig').read()
open(bom_file, mode='w', encoding='utf-8').write(s)
import codecs
import shutil
import sys
s = sys.stdin.read(3)
if s != codecs.BOM_UTF8:
sys.stdout.write(s)
shutil.copyfileobj(sys.stdin, sys.stdout)
Я нашел этот вопрос, потому что возникли проблемы с configparser.ConfigParser().read(fp)
при открытии файлов с заголовком UTF8 BOM.
Для тех, кто ищет решение для удаления заголовка, чтобы ConfigPhaser мог открыть файл конфигурации вместо сообщения об ошибке:File contains no section headers
Пожалуйста, откройте файл, как показано ниже:
configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")
Это может сэкономить массу усилий, сделав ненужным удаление заголовка спецификации файла.
(Я знаю, это звучит несвязанно, но, надеюсь, это может помочь людям, борющимся как я.)
Это моя реализация для преобразования любого вида кодирования в UTF-8 без спецификации и замены оконных рамок универсальным форматом:
def utf8_converter(file_path, universal_endline=True):
'''
Convert any type of file to UTF-8 without BOM
and using universal endline by default.
Parameters
----------
file_path : string, file path.
universal_endline : boolean (True),
by default convert endlines to universal format.
'''
# Fix file path
file_path = os.path.realpath(os.path.expanduser(file_path))
# Read from file
file_open = open(file_path)
raw = file_open.read()
file_open.close()
# Decode
raw = raw.decode(chardet.detect(raw)['encoding'])
# Remove windows end line
if universal_endline:
raw = raw.replace('\r\n', '\n')
# Encode to UTF-8
raw = raw.encode('utf8')
# Remove BOM
if raw.startswith(codecs.BOM_UTF8):
raw = raw.replace(codecs.BOM_UTF8, '', 1)
# Write to file
file_open = open(file_path, 'w')
file_open.write(raw)
file_open.close()
return 0
Вы можете использовать кодеки.
import codecs
with open("test.txt",'r') as filehandle:
content = filehandle.read()
if content[:3] == codecs.BOM_UTF8:
content = content[3:]
print content.decode("utf-8")
В python3 вы должны добавитьencoding='utf-8-sig'
:
with open(file_name, mode='a', encoding='utf-8-sig') as csvfile:
csvfile.writelines(rows)
вот и все.