Запишіть у файл UTF-8 на Python


204

Я справді плутаю з 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")?


54
Не використовуйте BOM в UTF-8. Будь ласка.
tchrist

7
@tchrist Так? Чому ні?
Салман фон Аббас

8
@SalmanPK BOM не потрібен в UTF-8 і лише додає складності (наприклад, ви не можете просто об'єднати BOM'd файли і отримати результат з дійсним текстом). Дивіться це питання та відповіді ; не пропустіть великий коментар під Q
Alois Mahdal

Відповіді:


271

Я вважаю, що проблема полягає в тому, що codecs.BOM_UTF8це байт-рядок, а не рядок Unicode. Я підозрюю, що обробник файлів намагається здогадатися, що ви насправді маєте на увазі "Я маю на увазі писати 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 BF.)

EDIT: Пропозиція С. Лотта використовувати "utf-8-sig" як кодування є кращою, ніж явно написати BOM самостійно, але я залишу цю відповідь тут, коли вона пояснює, що йшло не так раніше.


Попередження: відкрити і відкрити - це не те саме. Якщо ви робите "з імпорту кодеків відкритим", це НЕ буде таким, як ви просто ввели "відкрити".
Apache

2
ви також можете використати codecs.open ('test.txt', 'w', 'utf-8-sig') замість цього
бета-версія

1
Я отримую "TypeError: потрібне ціле число (got str str)". Я не розумію, що ми тут робимо. Може хтось, будь ласка, допоможе? Мені потрібно додати рядок (абзац) до текстового файлу. Чи потрібно мені, перш ніж писати, перетворити це в ціле число?
Муген

@Mugen: Точний код, який я написав, добре працює, наскільки я бачу. Я пропоную вам задати нове запитання, в якому вказано, який саме код у вас є, і де виникає помилка.
Джон Скіт

@Mugen вам потрібно зателефонувати codecs.openзамість простоopen
northben

179

Прочитайте наступне: 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 з очікуваною BOM.


2
Дякую. Це спрацювало (Windows 7 x64, Python 2.7.5 x64). Це рішення добре працює, коли ви відкриваєте файл у режимі "a" (додаток).
Mohamad Fakih

Це не спрацювало для мене, Python 3 у Windows. Я повинен був зробити це замість цього з відкритим (ім'я_файлу, 'wb') як bomfile: bomfile.write (codecs.BOM_UTF8), а потім знову відкрити файл для додавання.
Дастін Ендрюс

Може додати temp.close()?
user2905353

2
@ user2905353: не потрібно; це обробляється управління контекстом з open.
matheburg

11

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

Джон Скіт має рацію (незвично) щодо codecsмодуля - він містить рядки байтів:

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

Вибираючи ще одну нитку, у 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'
>>> 

8

Я використовую команду 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()

1
Використовувати # coding: utf8замість цього # -*- coding: utf-8 -*-запам'ятати набагато простіше.
show0k
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.