Differenza tra open e codecs.open in Python


95

Esistono due modi per aprire un file di testo in Python:

f = open(filename)

E

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

Quando è codecs.openpreferibile a open?


45
Notare che codecs.open()è obsoleto in 3.x, poiché open()ottiene un encodingargomento.
Ignacio Vazquez-Abrams

C'è anche un terzo modo (in Python 2.x almeno): `f = file (filename) '
Adam Parkin

1
@ IgnacioVazquez-Abrams C'è qualche collegamento codecs.open()obsoleto? Non credo che questo nel docs python3: docs.python.org/3.7/library/codecs.html
Varela

1
@varela: la pagina della documentazione di Python che hai citato dice: "il builtin open () e il modulo io associato sono l'approccio consigliato per lavorare con file di testo codificati"
Luciano Ramalho

Risposte:


83

A partire da Python 2.6, una buona pratica è usare io.open(), che accetta anche un encodingargomento, come l'ormai obsoleto codecs.open(). In Python 3, io.openè un alias per il open()built-in. Quindi io.open()funziona in Python 2.6 e tutte le versioni successive, incluso Python 3.4. Vedi la documentazione: http://docs.python.org/3.4/library/io.html

Ora, per la domanda originale: quando leggi del testo (incluso "testo normale", HTML, XML e JSON) in Python 2 dovresti sempre usarlo io.open()con una codifica esplicita o open()con una codifica esplicita in Python 3. Ciò significa che ottieni correttamente decodificato Unicode, o ricevi subito un errore, rendendo molto più facile il debug.

Il puro "testo normale" ASCII è un mito del lontano passato. Il testo inglese corretto utilizza virgolette ricce, trattini emem, punti elenco, € (segni di euro) e persino dieresi (¨). Non essere ingenuo! (E non dimentichiamo il motivo di design della facciata!)

Poiché il puro ASCII non è un'opzione reale, open()senza una codifica esplicita è utile solo per leggere i file binari .


5
@ForeverWintr La risposta è abbastanza chiara: usa io.open()per il testo e open()solo per il binario. L'implicazione è che codecs.open()non è affatto preferito.
Bdoserror

2
@Bdoserror, C'è una risposta lì, chiaramente, ma non è una risposta alla domanda che è stata posta. La domanda riguardava la differenza tra opene codecs.open, e in particolare quando il secondo è preferibile al primo. Una risposta che non è nemmeno menzionata codecs.opennon può rispondere a questa domanda.
ForeverWintr

3
@ForeverWintr Se l'OP ha posto la domanda sbagliata (cioè con l'ipotesi che codecs.open()fosse corretto da usare) allora non c'è una risposta "corretta" su quando usarla. La risposta è io.open()invece usare . È come se chiedessi "quando devo usare una chiave inglese per piantare un chiodo in un muro?". La risposta giusta è "usa un martello".
Bdoserror

20

Personalmente, lo uso sempre acodecs.open meno che non ci sia una chiara esigenza identificata da usare open**. Il motivo è che ci sono state così tante volte in cui sono stato morso dall'input di utf-8 che si intrufola nei miei programmi. "Oh, so solo che sarà sempre ascii" tende ad essere un presupposto che viene infranto spesso.

Supponendo 'utf-8' come codifica predefinita tende ad essere una scelta predefinita più sicura nella mia esperienza, poiché ASCII può essere trattato come UTF-8, ma il contrario non è vero. E in quei casi in cui ho veramente non so che l'ingresso è ASCII, allora faccio ancora codecs.opencome io sono un convinto sostenitore "esplicito è meglio che implicito" .

** - in Python 2.x, come il commento sulla domanda afferma in Python 3 opensostituiscecodecs.open


quello che non capisco davvero è perché a openvolte riesca a gestire molto bene i caratteri non latini codificati UTF-8 del set unicode, e talvolta fallisce
miseramente

Questo ha senso per me. io.opennon prende un parametro di codifica da quello che posso vedere in Python 2.7.5
radtek

1
@radtek, hai ragione sul fatto che non è documentato; tuttavia (almeno in 2.7.12) io.openaccetta encodinge newlineparametri e li interpreta come Python 3 fa. Diversamente codecs.open, un file aperto con io.opensolleverà TypeError: write() argument 1 must be unicode, not stranche in Python 2.7 se provi a scriverci str( bytes). Un file aperto con codecs.opententerà invece la conversione implicita in unicode, spesso portando a confondere UnicodeDecodeErrors.
jochietoch

9

In Python 2 ci sono stringhe Unicode e bytestrings. Se usi solo bytestrings, puoi leggere / scrivere su un file aperto con open(). Dopo tutto, le stringhe sono solo byte.

Il problema sorge quando, ad esempio, hai una stringa Unicode e fai quanto segue:

>>> 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)

Quindi qui ovviamente o codifichi esplicitamente la tua stringa unicode in utf-8 o lo usi codecs.openper farlo in modo trasparente.

Se utilizzi solo bytestrings, nessun problema:

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

Diventa più complicato di questo perché quando concateni una stringa unicode e bytestring con l' +operatore ottieni una stringa unicode. È facile farsi mordere da quello.

Inoltre codecs.opennon gradisce il passaggio di bytestrings con caratteri non ASCII:

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)

Il consiglio sulle stringhe per input / output è normalmente "converti in unicode il prima possibile e torna in bytestrings il più tardi possibile". L' codecs.openutilizzo ti consente di eseguire quest'ultimo molto facilmente.

Fai solo attenzione a dargli stringhe Unicode e non stringhe che potrebbero avere caratteri non ASCII.


Puoi spiegare il tuo secondo esempio? Sembra essere identico al tuo primo esempio, quindi perché il risultato dovrebbe essere diverso?
Chris Johnson,

Notare l'uso di u''nel primo esempio. Ciò significa che ho creato una stringa Unicode, non una bytestring. Questa è la differenza tra i due esempi. Nel secondo esempio sto creando una bytestring e scrivere uno di questi su un file va bene. Una stringa Unicode non va bene se stai usando caratteri al di fuori di ASCII.
Mandible79

7

Quando hai bisogno di aprire un file che ha una certa codifica, dovresti usare il codecsmodulo.


15
Immagino che tutti i file di testo abbiano una certa codifica, in qualche modo (:
cedbeu

5

codecs.open, suppongo, è solo un residuo dei Python 2giorni in cui l'open integrato aveva un'interfaccia molto più semplice e meno funzionalità. In Python 2, built-in opennon accetta un argomento di codifica, quindi se vuoi usare qualcosa di diverso dalla modalità binaria o dalla codifica predefinita, dovrebbe essere usato codecs.open.

In Python 2.6, il modulo io è venuto in aiuto per rendere le cose un po 'più semplici. Secondo la documentazione ufficiale

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.

Detto questo, l'unico utilizzo che mi viene in mente codecs.opennello scenario attuale è per la compatibilità con le versioni precedenti. In tutti gli altri scenari (a meno che tu non stia usando Python <2.6) è preferibile usare io.open. Anche in Python 3.x io.openè lo stesso dibuilt-in open

Nota:

C'è anche una differenza sintattica tra codecs.opene 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)

Non solo codecs.opene io.opendifferiscono in termini di sintassi, restituiscono oggetti di diverso tipo. Inoltre codecs.openfunziona sempre con i file in modalità binaria.
wombatonfire

4
  • Quando vuoi caricare un file binario, usa f = io.open(filename, 'b').

  • Per aprire un file di testo, utilizzare sempre f = io.open(filename, encoding='utf-8')con codifica esplicita.

In python 3 tuttavia openfa la stessa cosa di io.opene può essere usato al suo posto.

Nota: codecs.open è previsto che diventi deprecato e sostituito da io.opendopo la sua introduzione in python 2.6 . Lo userei solo se il codice deve essere compatibile con le versioni precedenti di Python. Per ulteriori informazioni su codec e unicode in python, vedere Unicode HOWTO .


1. Perché non riesco ad aprire un file in modalità binaria con io.openo codecs.open? 2. codecs.opennon è ancora deprecato, leggi la discussione nella pagina a cui ti sei collegato.
wombatonfire

Punti buoni! 1. Puoi usare entrambi, ma consiglierei di nuovo contro codecs.open a meno che tu non sia su Python 2.5 o precedente. 2. Ho aggiornato la mia risposta per riflettere che la deprecazione non è avvenuta immediatamente, ma piuttosto in futuro.
entro

3

Quando lavori con file di testo e desideri una codifica e una decodifica trasparenti in oggetti Unicode.


0

Ero in una situazione per aprire un file .asm ed elaborare il file.

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

Senza troppi problemi riesco a leggere l'intero file, qualche suggerimento?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.