Запись в файл UTF-8 на Python


Я действительно путаю с codecs.open function. Когда я это сделаю:

file = codecs.open("temp", "w", "utf-8")
file.write(codecs.BOM_UTF8)
file.close()

Это дает мне ошибку

UnicodeDecodeError: 'в кодировке ASCII' кодек не может декодировать байт 0xef в положении 0: порядковый номер не в диапазоне (128)

Если я это сделаю:

file = open("temp", "w")
file.write(codecs.BOM_UTF8)
file.close()

Это прекрасно работает.

Вопрос состоит в том, почему первый метод терпит неудачу? И как мне вставить спецификацию?

Если второй метод является правильным способом его выполнения, какой смысл использовать codecs.open(filename, "w", "utf-8")?

4 154

4 ответа:

Я полагаю, что проблема заключается в том, что codecs.BOM_UTF8 является строкой байтов, а не строкой Юникода. Я подозреваю, что обработчик файлов пытается угадать, что вы на самом деле имеете в виду, основываясь на "Я должен писать Unicode как кодированный UTF-8 текст, но вы дали мне байтовую строку!"

Попробуйте записать строку Unicode для метки порядка байтов (то есть Unicode U+FEFF) напрямую, чтобы файл просто кодировал ее как UTF-8:

import codecs

file = codecs.open("lol", "w", "utf-8")
file.write(u'\ufeff')
file.close()

(похоже, это дает правильный ответ-файл с байтами EF BB БФ.)

EDIT: предложение С. Лотта использовать "utf-8-sig" в качестве кодировки лучше, чем явно писать BOM самостоятельно, но я оставлю этот ответ здесь, поскольку он объясняет, что было неправильно раньше.

Прочитайте следующее: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Сделай это

with codecs.open("test_output", "w", "utf-8-sig") as temp:
    temp.write("hi mom\n")
    temp.write(u"This has ♭")

Результирующий файл-UTF-8 с ожидаемой спецификацией.

@S-Lott дает правильную процедуру, но расширяя вопросы Unicode, интерпретатор Python может дать больше информации.

Джон Скит прав (необычно) насчет codecs модуль-содержит байтовые строки:

>>> import codecs
>>> codecs.BOM
'\xff\xfe'
>>> codecs.BOM_UTF8
'\xef\xbb\xbf'
>>> 

Выбирая другой nit, BOM имеет стандартноеUnicode имя, и его можно ввести как:

>>> bom= u"\N{ZERO WIDTH NO-BREAK SPACE}"
>>> bom
u'\ufeff'

Он также доступен через unicodedata:

>>> import unicodedata
>>> unicodedata.lookup('ZERO WIDTH NO-BREAK SPACE')
u'\ufeff'
>>> 

Я использую команду file *nix для преобразования неизвестного файла кодировки в файл utf-8

# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()