2009-06-01 12 views
145

Naprawdę jestem zdezorientowany z codecs.open function. Kiedy zrobić:Zapisywanie do pliku UTF-8 w Pythonie

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

Daje mi błąd

UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 0: ordinal not in range(128)

Jeśli zrobić:

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

To działa dobrze.

Pytanie dlaczego pierwsza metoda zawodzi? A jak wstawić bom?

Jeśli druga metoda jest prawidłowym sposobem wykonania, jaki jest cel używania codecs.open(filename, "w", "utf-8")?

+40

Nie używaj BOM UTF-8. *** Proszę. *** – tchrist

+6

@tchrist Huh?Dlaczego nie? –

+6

@SalmanPK BOM nie jest potrzebny w UTF-8 i tylko dodaje złożoność (np. Nie można po prostu połączyć zbiorów LM i uzyskać poprawnego tekstu). Zobacz [ten Q & A] (http://stackoverflow.com/questions/2223882/whats-different-weenween-utf-8-and-utf-8-without-bom); nie przegap wielkiego komentarza pod Q –

Odpowiedz

213

Wierzę, że problem polega na tym, że codecs.BOM_UTF8 jest ciągiem bajtów, a nie ciągiem znaków Unicode. Podejrzewam, że program obsługi plików próbuje odgadnąć, co naprawdę masz na myśli, mówiąc: "Mam zamiar pisać Unicode jako tekst zakodowany w UTF-8, ale dałeś mi ciąg bajtów!"

Spróbuj napisać ciąg Unicode dla bom (czyli Unicode U + FEFF) bezpośrednio, tak, że plik po prostu koduje że jako UTF-8

import codecs 

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

(To wydaje się dać właściwą odpowiedź - plik z bajtem EF BB BF.)

EDYCJA: S. Lott's suggestion z użyciem "utf-8-sig", ponieważ kodowanie jest lepsze niż jawne samodzielne napisanie BOM, ale zostawię tę odpowiedź tutaj, ponieważ wyjaśnia to, co działo się wcześniej.

+0

Bardzo dziękuję za to, zdecydowanie zrobił rzeczy jaśniejsze –

+0

Ostrzeżenie: otwarte i otwarte nie jest to samo. Jeśli zrobisz "z otwartego importu kodeków", NIE będzie to to samo co po prostu wpisz "otwórz". – Shiki

+0

możesz również użyć codecs.open ("test.txt", "w", "utf-8-sig") zamiast: –

150

przeczytać: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Wykonaj

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

plik wynikowy jest UTF-8 z oczekiwanym BOM.

+1

Dzięki. To zadziałało (Windows 7 x64, Python 2.7.5 x64). To rozwiązanie działa dobrze, gdy otworzysz plik w trybie "a" (dołącz). –

+0

"Najpierw zaimportuj kodeki". – KrisWebDev

+0

To nie działa dla mnie, Python 3 w systemie Windows. Musiałem to zrobić zamiast tego z open (file_name, 'wb') jako bomfile: bomfile.write (codecs.BOM_UTF8), a następnie ponownie otwórz plik do dołączenia. –

11

@ S-Lott daje odpowiednią procedurę, ale rozszerza się na Unicode kwestii, interpreter Pythona może dostarczyć więcej spostrzeżeniami.

Jon Skeet ma rację (nietypowe) o module codecs - zawiera ciągi bajtów:

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

Picking inny nit The BOM posiada standardową Unicode imię, a to może być wpisany jako:

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

jest również dostępny za pośrednictwem unicodedata:

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

Próbowałem wzbogacić twoją odpowiedź, zachowując ducha. – tzot

5

użyć polecenia Plik * nix przekonwertować plik nieznanego charset w UTF-8 pliku

# -*- 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() 
+0

W dzisiejszych czasach można również użyć [chardet] (https://pypi.python.org/pypi/chardet). –

+0

Użyj '# coding: utf8' zamiast' # - * - coding: utf-8 - * - 'który jest o wiele łatwiejszy do zapamiętania. – show0k