2009-06-01 7 views
145

Ich bin wirklich mit der codecs.open function verwechselt. Wenn ich tun:Schreiben in UTF-8-Datei in Python

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

Es gibt mir den Fehler

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

Wenn ich tun:

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

Es funktioniert gut.

Frage ist warum schlägt die erste Methode fehl? Und wie füge ich den bom ein?

Wenn die zweite Methode der richtige Weg ist, was ist der Sinn der Verwendung von codecs.open(filename, "w", "utf-8")?

+40

konvertieren Sie keine BOM in 8 UTF-verwenden. *** Bitte. *** – tchrist

+6

@tchrist Huh?Warum nicht? –

+6

@SalmanPK BOM wird in UTF-8 nicht benötigt und erhöht nur die Komplexität (z. B. können Sie nicht nur BOM-Dateien verketten und mit gültigem Text versehen). Siehe [diese Q & A] (http://stackoverflow.com/questions/2223882/whats-different-between-utf-8-and-utf-8-without-bom); Verpassen Sie nicht den großen Kommentar unter Q –

Antwort

213

Ich glaube, das Problem ist, dass codecs.BOM_UTF8 ist eine Byte-Zeichenfolge, keine Unicode-Zeichenfolge. Ich vermute, dass der Datei-Handler versucht zu erraten, was Sie wirklich meinen, basierend auf "Ich soll Unicode als UTF-8-kodierten Text schreiben, aber Sie haben mir eine Byte-Zeichenfolge gegeben!"

Versuchen Sie, die Unicode-Zeichenfolge für die Bytereihenfolgemarkierung (dh Unicode U + FEFF) direkt zu schreiben, so dass die Datei kodiert nur, dass als UTF-8:

import codecs 

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

(Das scheint die richtige Antwort zu geben, - eine Datei mit Bytes EF BB BF)

EDIT: S. Lott suggestion der Verwendung von „utf-8-sig“, wie die Codierung besser ist eine als ausdrücklich die BOM Schreiben selbst, aber ich werde diese Antwort verlassen. Hier wird erklärt, was vorher schief gelaufen ist.

+0

Vielen Dank dafür, hat definitiv die Dinge klarer gemacht –

+0

Warnung: Öffnen und Öffnen ist nicht das Gleiche. Wenn Sie "von Codecs importieren öffnen", wird es nicht das gleiche wie Sie würden einfach "Öffnen" eingeben. – Shiki

+0

können Sie auch codecs.open ('test.txt', 'w', 'utf-8-sig') statt –

150

folgend lesen: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

diesen Do

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

Die resultierende Datei ist UTF-8 mit der erwarteten Stückliste.

+1

Vielen Dank. Das hat funktioniert (Windows 7 x64, Python 2.7.5 x64). Diese Lösung funktioniert gut, wenn Sie die Datei im Modus "a" (append) öffnen. –

+0

'zuerst Codecs importieren '. – KrisWebDev

+0

Das hat bei mir nicht funktioniert, Python 3 unter Windows. Ich musste dies stattdessen mit öffnen (file_name, 'wb') als bomfile: bomfile.write (codecs.BOM_UTF8) dann öffnen Sie die Datei zum Anhängen. –

11

@ S-Lott gibt das richtige Verfahren, sondern erweitert auf den Unicode Fragen, die Python Interpreter kann mehr Erkenntnisse.

Jon Skeet ist richtig (ungewöhnlich) über das codecs Modul - es Byte-Strings enthält:

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

andere nit Kommissionierung, die BOM einen Standard Unicode Namen hat, und es kann als eingegeben werden:

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

Es ist auch über unicodedata:

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

Ich habe versucht, deine Antwort zu bereichern und dabei deinen Geist zu bewahren. – tzot

5

Ich verwende die * nix-Befehlsdatei eine unbekannte charset-Datei in einer utf-8-Datei

# -*- 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

Heutzutage können Sie auch [chardet] (https://pypi.python.org/pypi/chardet) verwenden. –

+0

Verwenden Sie '# coding: utf8' anstelle von' # - * - coding: utf-8 - * - 'was viel einfacher zu merken ist. – show0k