Unterschied zwischen open und codecs.open in Python

93

Es gibt zwei Möglichkeiten, eine Textdatei in Python zu öffnen:

f = open(filename)

Und

import codecs
f = codecs.open(filename, encoding="utf-8")

Wann ist es codecs.openvorzuziehen open?

BlogueroConnor
quelle
44
Beachten Sie, dass dies codecs.open()in 3.x veraltet ist, da open()ein encodingArgument gewonnen wird.
Ignacio Vazquez-Abrams
Es gibt auch einen dritten Weg (mindestens in Python 2.x): `f = Datei (Dateiname) '
Adam Parkin
1
@ IgnacioVazquez-Abrams Gibt es einen Link, der codecs.open()veraltet ist? Ich denke nicht, dass dies in Python3-Dokumenten: docs.python.org/3.7/library/codecs.html
Varela
1
@varela: Auf der von Ihnen erwähnten Python-Dokumentationsseite heißt es: "Das eingebaute open () und das zugehörige io-Modul sind der empfohlene Ansatz für die Arbeit mit codierten Textdateien"
Luciano Ramalho,

Antworten:

82

Seit Python 2.6 ist eine gute Praxis zu verwenden io.open(), die auch ein encodingArgument wie das jetzt veraltete nimmt codecs.open(). In Python 3 io.openist ein Alias ​​für das open()eingebaute. Funktioniert also io.open()in Python 2.6 und allen späteren Versionen, einschließlich Python 3.4. Siehe Dokumente: http://docs.python.org/3.4/library/io.html

Nun, für die ursprüngliche Frage: beim Lesen Text (einschließlich „Klartext“, HTML, XML und JSON) in Python 2 Sie sollten immer verwenden io.open()mit einer expliziten Kodierung oder open()mit einer expliziten Kodierung in Python 3. Doing so Mittel erhalten Sie richtig Unicode dekodiert oder sofort einen Fehler angezeigt, was das Debuggen erheblich erleichtert.

Reiner ASCII "Klartext" ist ein Mythos aus der fernen Vergangenheit. Der richtige englische Text verwendet geschweifte Anführungszeichen, Striche, Aufzählungszeichen, € (Euro-Zeichen) und sogar Diaeresis (¨). Sei nicht naiv! (Und vergessen wir nicht das Fassadenmuster!)

Da reines ASCII keine echte Option ist, ist open()eine explizite Codierung nur zum Lesen von Binärdateien sinnvoll .

Luciano Ramalho
quelle
5
@ForeverWintr Die Antwort ist ziemlich klar: Verwenden Sie sie io.open()für Text und open()nur für Binärdateien. Die Implikation ist, dass dies überhaupt codecs.open()nicht bevorzugt wird.
Bdoserror
2
@Bdoserror, da ist eindeutig eine Antwort drin, aber es ist keine Antwort auf die gestellte Frage. Die Frage betraf den Unterschied zwischen openund codecs.openund insbesondere, wann letzteres dem ersteren vorzuziehen ist. Eine Antwort, die nicht einmal erwähnt wird, codecs.openkann diese Frage nicht beantworten.
ForeverWintr
3
@ForeverWintr Wenn das OP die falsche Frage gestellt hat (dh mit der Annahme, dass codecs.open()die Verwendung korrekt war), gibt es keine "richtige" Antwort darauf, wann sie verwendet werden soll. Die Antwort ist io.open()stattdessen zu verwenden . Es ist wie wenn ich frage "Wann sollte ich einen Schraubenschlüssel verwenden, um einen Nagel in eine Wand zu schlagen?". Die richtige Antwort lautet "Hammer benutzen".
Bdoserror
20

Persönlich benutze ich immer , es codecs.opensei denn, es gibt einen klar identifizierten Bedarf open**. Der Grund ist, dass ich so oft gebissen wurde, weil sich utf-8-Eingaben in meine Programme eingeschlichen haben. "Oh, ich weiß nur, dass es immer ASCII sein wird" ist eine Annahme, die oft gebrochen wird.

Die Annahme von 'utf-8' als Standardcodierung ist meiner Erfahrung nach eine sicherere Standardauswahl, da ASCII als UTF-8 behandelt werden kann, aber das Gegenteil ist nicht der Fall. Und in jenen Fällen, in denen ich wirklich weiß , dass die Eingabe ASCII ist, tue ich dies immer noch, codecs.openda ich fest davon überzeugt bin, dass "explizit besser als implizit" ist .

** - in Python 2.x, wie der Kommentar zu der Frage in Python 3 openersetztcodecs.open

Adam Parkin
quelle
Was ich nicht wirklich verstehe, ist, warum openmanchmal sehr gut mit den UTF-8-codierten nicht-lateinischen Zeichen des Unicode-Sets
umgegangen werden kann
Das macht für mich Sinn. io.opennimmt keinen Kodierungsparameter von dem, was ich in Python 2.7.5 sehen kann
radtek
1
@radtek, Sie haben Recht, dass dies nicht dokumentiert ist; (zumindest in 2.7.12) io.openakzeptiert encodingund newlineparametrisiert sie jedoch und interpretiert sie wie Python 3. Im Gegensatz zu codecs.open, eine Datei geöffnet mit io.openerhöhen wird TypeError: write() argument 1 must be unicode, not strauch in Python 2.7 , wenn Sie zu schreiben versuchen str( bytes) zu. Eine mit geöffnete Datei codecs.openversucht stattdessen eine implizite Konvertierung in unicode, was häufig zu verwirrenden UnicodeDecodeErrors führt.
Jochietoch
9

In Python 2 gibt es Unicode-Zeichenfolgen und Bytestrings. Wenn Sie nur Bytestrings verwenden, können Sie in eine Datei lesen / schreiben, die mit open()ganz gut geöffnet wurde . Immerhin sind die Zeichenfolgen nur Bytes.

Das Problem tritt auf, wenn Sie beispielsweise eine Unicode-Zeichenfolge haben und Folgendes tun:

>>> example = u'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Hier codieren Sie also offensichtlich entweder explizit Ihre Unicode-Zeichenfolge in utf-8 oder Sie verwenden codecs.opendies, um dies transparent für Sie zu tun.

Wenn Sie immer nur Bytestrings verwenden, gibt es keine Probleme:

>>> example = 'Μου αρέσει Ελληνικά'
>>> open('sample.txt', 'w').write(example)
>>>

Dies ist komplizierter, da beim Verketten einer Unicode- und Bytestring-Zeichenfolge mit dem +Operator eine Unicode-Zeichenfolge angezeigt wird . Leicht von diesem gebissen zu werden.

codecs.openMag auch keine Bytestrings mit Nicht-ASCII-Zeichen, die übergeben werden:

codecs.open('test', 'w', encoding='utf-8').write('Μου αρέσει')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/codecs.py", line 691, in write
    return self.writer.write(data)
  File "/usr/lib/python2.7/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xce in position 0: ordinal not in range(128)

Der Ratschlag zu Zeichenfolgen für Eingabe / Ausgabe lautet normalerweise "so früh wie möglich in Unicode konvertieren und so spät wie möglich zurück in Bytestrings". Mit codecs.openkönnen Sie Letzteres sehr einfach tun.

Achten Sie nur darauf, dass Sie ihm Unicode-Zeichenfolgen und keine Bytestrings geben, die möglicherweise Nicht-ASCII-Zeichen enthalten.

Mandible79
quelle
Können Sie Ihr zweites Beispiel erklären? Es scheint mit Ihrem ersten Beispiel identisch zu sein. Warum sollte das Ergebnis anders sein?
Chris Johnson
Beachten Sie die Verwendung von u''im ersten Beispiel. Dies bedeutet, dass ich eine Unicode-Zeichenfolge erstellt habe, keinen Bytestring. Dies ist der Unterschied zwischen den beiden Beispielen. Im zweiten Beispiel erstelle ich einen Bytestring und das Schreiben eines davon in eine Datei ist in Ordnung. Eine Unicode-Zeichenfolge ist nicht in Ordnung, wenn Sie Zeichen außerhalb von ASCII verwenden.
Mandible79
7

Wenn Sie eine Datei mit einer bestimmten Codierung öffnen müssen, verwenden Sie das codecsModul.

Geo
quelle
15
Ich denke, alle Textdateien haben irgendwie eine bestimmte Codierung (:
Cedbeu
5

codecs.openIch nehme an, es ist nur ein Überbleibsel aus der Python 2Zeit, als das eingebaute Open eine viel einfachere Oberfläche und weniger Funktionen hatte. In Python 2 wird für opendie integrierte Funktion kein Codierungsargument verwendet. Wenn Sie also etwas anderes als den Binärmodus oder die Standardcodierung verwenden möchten, sollte codecs.open verwendet werden.

In Python 2.6kam das io-Modul zur Hilfe, um die Dinge ein bisschen einfacher zu machen. Nach der offiziellen Dokumentation

New in version 2.6.

The io module provides the Python interfaces to stream handling.
Under Python 2.x, this is proposed as an alternative to the
built-in file object, but in Python 3.x it is the default
interface to access files and streams.

Trotzdem kann ich mir codecs.openim aktuellen Szenario nur die Abwärtskompatibilität vorstellen. In allen anderen Szenarien (es sei denn, Sie verwenden Python <2.6) ist die Verwendung vorzuziehen io.open. Auch in Python 3.x io.openist das gleiche wiebuilt-in open

Hinweis:

Es gibt auch einen syntaktischen Unterschied zwischen codecs.openund io.open.

codecs.open::

open(filename, mode='rb', encoding=None, errors='strict', buffering=1)

io.open::

open(file, mode='r', buffering=-1, encoding=None,
     errors=None, newline=None, closefd=True, opener=None)
heretolearn
quelle
Nicht nur codecs.openund io.openunterscheiden sich in der Syntax, kehren sie Objekte unterschiedlicher Art. Funktioniert auch codecs.openimmer mit Dateien im Binärmodus.
Wombatonfire
4
  • Wenn Sie eine Binärdatei laden möchten, verwenden Sie f = io.open(filename, 'b').

  • Verwenden Sie zum Öffnen einer Textdatei immer die f = io.open(filename, encoding='utf-8')explizite Codierung.

In Python 3open funktioniert dies jedoch genauso wie io.openund kann stattdessen verwendet werden.

Hinweis: Es codecs.open ist geplant, dass es nach seiner Einführung in Python 2.6 veraltet und ersetzt wird . Ich würde es nur verwenden, wenn Code mit früheren Python-Versionen kompatibel sein muss. Weitere Informationen zu Codecs und Unicode in Python finden Sie im Unicode-HOWTO .io.open

wihlke
quelle
1. Warum kann ich eine Datei im Binärmodus nicht mit io.openoder öffnen codecs.open? 2. codecs.openist noch nicht veraltet, lesen Sie die Diskussion auf der Seite, auf die Sie verlinkt haben.
Wombatonfire
Gute Argumente! 1. Sie können beides verwenden, aber ich würde erneut von codecs.open abraten, es sei denn, Sie verwenden Python 2.5 oder älter. 2. Ich habe meine Antwort aktualisiert, um zu berücksichtigen, dass die Abschreibung nicht sofort, sondern in der Zukunft erfolgte.
wihlke
3

Wenn Sie mit Textdateien arbeiten und eine transparente Codierung und Decodierung in Unicode-Objekte wünschen.

Cat Plus Plus
quelle
0

Ich war in der Lage, eine .asm-Datei zu öffnen und die Datei zu verarbeiten.

#https://docs.python.org/3/library/codecs.html#codecs.ignore_errors
#https://docs.python.org/3/library/codecs.html#codecs.Codec.encode
with codecs.open(file, encoding='cp1252', errors ='replace') as file:

Ohne viel Mühe kann ich die gesamte Datei lesen, irgendwelche Vorschläge?

Gowtham Ch
quelle