2009-06-01 23 views
145

Sono davvero confuso con lo codecs.open function. Quando faccio:Scrittura su file UTF-8 in Python

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

Mi dà l'errore

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

Se lo faccio:

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

Funziona benissimo.

Domanda perché il primo metodo fallisce? E come inserisco il bom?

Se il secondo metodo è il modo corretto di farlo, quale punto utilizzare codecs.open(filename, "w", "utf-8")?

+40

Non utilizzare una distinta in UTF-8. *** Per favore. *** – tchrist

+6

@tchrist Huh?Perchè no? –

+6

@SalmanPK BOM non è necessario in UTF-8 e aggiunge solo complessità (ad esempio non è possibile concatenare solo i file BOM e ottenere un testo valido). Vedi [questo Q & A] (http://stackoverflow.com/questions/2223882/whats-different-tra between-utf-8-and-utf-8-without-bom); non perdetevi il grande commento sotto Q –

risposta

213

Credo che il problema è che codecs.BOM_UTF8 è una stringa di byte, non una stringa Unicode. Sospetto che il gestore di file stia cercando di indovinare cosa intendi in base a "Io sono destinato a scrivere Unicode come testo con codifica UTF-8, ma mi hai dato una stringa di byte!"

Provare a scrivere la stringa Unicode per il segno di ordine dei byte (ad esempio Unicode U + FEFF) direttamente, in modo che il file appena codifica che, come UTF-8:

import codecs 

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

(Che sembra dare la risposta giusta - un file con byte EF BB BF.)

MODIFICA: S. Lott's suggestion dell'utilizzo di "utf-8-sig" poiché la codifica è migliore di quella che si scrive esplicitamente il BOM, ma lascerò questa risposta qui come spiega cosa stava andando storto prima.

+0

Grazie mille per questo, ha sicuramente reso le cose più chiare –

+0

Attenzione: aprire e aprire non è la stessa cosa. Se esegui "dall'importazione dei codecs aperti", NON sarà lo stesso come se fosse semplicemente "aperto". – Shiki

+0

puoi anche usare codecs.open ('test.txt', 'w', 'utf-8-sig') invece –

150

leggere quanto segue: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

fare questo

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

Il file risultante è UTF-8 con l'atteso BOM.

+1

Grazie. Funzionava (Windows 7 x64, Python 2.7.5 x64). Questa soluzione funziona bene quando apri il file in modalità "a" (append). –

+0

'import codecs' prima. – KrisWebDev

+0

Questo non ha funzionato per me, Python 3 su Windows. Ho dovuto farlo invece con open (file_name, 'wb') come bomfile: bomfile.write (codecs.BOM_UTF8) quindi riaprire il file per append. –

11

@ S-Lott dà la procedura corretta, ma in espansione sui Unicode problemi, il Python interprete in grado di fornire ulteriori approfondimenti.

Jon Skeet è giusto (insolito) sul modulo codecs - contiene stringhe di byte:

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

Picking un'altra nit, il BOM ha uno standard nome Unicode, e può essere inserito come:

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

E 'accessibile anche tramite unicodedata:

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

Ho cercato di arricchire la tua risposta mantenendo il tuo spirito. – tzot

5

Io uso il comando file * nix per convertire un file charset sconosciuto in un file 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() 
+0

Al giorno d'oggi, è possibile utilizzare anche [chardet] (https://pypi.python.org/pypi/chardet). –

+0

Usa '# coding: utf8' invece di' # - * - coding: utf-8 - * - 'che è molto più facile da ricordare. – show0k