Diferencia entre open y codecs.open en Python

95

Hay dos formas de abrir un archivo de texto en Python:

f = open(filename)

Y

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

¿Cuándo es codecs.openpreferible open?

BlogueroConnor
fuente
45
Tenga en cuenta que codecs.open()está obsoleto en 3.x, ya que open()gana un encodingargumento.
Ignacio Vazquez-Abrams
También hay una tercera forma (en Python 2.x al menos): `f = file (filename) '
Adam Parkin
1
@ IgnacioVazquez-Abrams ¿Hay algún enlace codecs.open()obsoleto? No creo que esto en los documentos de python3
varela
1
@varela: la página de documentación de Python que mencionaste dice: "el open () incorporado y el módulo io asociado son el enfoque recomendado para trabajar con archivos de texto codificados"
Luciano Ramalho

Respuestas:

83

Desde Python 2.6, una buena práctica es usar io.open(), que también toma un encodingargumento, como el ahora obsoleto codecs.open(). En Python 3, io.openes un alias para el open()archivo integrado. Así que io.open()funciona en Python 2.6 y todas las versiones posteriores, incluido Python 3.4. Ver documentos: http://docs.python.org/3.4/library/io.html

Ahora, para la pregunta original: al leer texto (incluido "texto sin formato", HTML, XML y JSON) en Python 2, siempre debe usarlo io.open()con una codificación explícita o open()con una codificación explícita en Python 3. Si lo hace, significa que obtiene correctamente decodificado Unicode, o obtiene un error de inmediato, lo que facilita la depuración.

El "texto sin formato" ASCII puro es un mito del pasado distante. El texto en inglés adecuado utiliza comillas entrecruzadas, guiones en forma de em, viñetas, € (signos de euro) e incluso diéresis (¨). ¡No seas ingenuo! (¡Y no olvidemos el patrón de diseño de fachada!)

Debido a que ASCII puro no es una opción real, open()sin una codificación explícita solo es útil para leer archivos binarios .

Luciano Ramalho
fuente
5
@ForeverWintr La respuesta está bastante clara allí: se usa io.open()para texto, y open()solo para binario. La implicación es que codecs.open()no se prefiere en absoluto.
Bdoserror
2
@Bdoserror, hay una respuesta ahí, claramente, pero no es una respuesta a la pregunta que se hizo. La pregunta era sobre la diferencia entre openy codecs.open, y específicamente cuándo es preferible este último al primero. Una respuesta que ni siquiera menciona codecs.openno puede responder esa pregunta.
ForeverWintr
3
@ForeverWintr Si el OP hizo la pregunta incorrecta (es decir, con la suposición de que codecs.open()era correcto usarlo), entonces no hay una respuesta "correcta" sobre cuándo usarlo. La respuesta es usar io.open()en su lugar. Es como si preguntara "¿cuándo debo usar una llave para clavar un clavo en una pared?". La respuesta correcta es "usa un martillo".
Bdoserror
20

Personalmente, siempre uso a codecs.openmenos que haya una clara necesidad identificada de usar open**. La razón es que ha habido muchas ocasiones en las que me ha mordido que la entrada de utf-8 se cuela en mis programas. "Oh, solo sé que siempre será ascii" tiende a ser una suposición que se rompe a menudo.

Asumir 'utf-8' como la codificación predeterminada tiende a ser una opción predeterminada más segura en mi experiencia, ya que ASCII se puede tratar como UTF-8, pero lo contrario no es cierto. Y en aquellos casos en los que realmente que la entrada es ASCII, lo sigo haciendo, codecs.openya que creo firmemente en "lo explícito es mejor que lo implícito" .

** - en Python 2.x, como el comentario sobre la pregunta dice en Python 3 openreemplazacodecs.open

Adam Parkin
fuente
lo que realmente no entiendo es por qué a openveces puede manejar muy bien los caracteres no latinos codificados en UTF-8 del conjunto Unicode, y a veces falla
miserablemente
Esto tiene sentido para mí. io.openno toma un parámetro de codificación de lo que puedo ver en Python
2.7.5
1
@radtek, tienes razón en que esto no está documentado; sin embargo (al menos en 2.7.12) io.openacepta encodingy newlineparámetros y interpreta como Python 3 hace. A diferencia codecs.open, un archivo abierto con io.openaumentará TypeError: write() argument 1 must be unicode, not strincluso en Python 2.7 si intenta escribir str( bytes) en él. En su lugar, un archivo abierto con codecs.openintentará una conversión implícita a unicode, lo que a menudo genera confusiones UnicodeDecodeError.
jochietoch
9

En Python 2 hay cadenas unicode y cadenas de bytes. Si solo usa cadenas de bytes, puede leer / escribir en un archivo abierto con open()muy bien. Después de todo, las cadenas son solo bytes.

El problema surge cuando, digamos, tiene una cadena Unicode y hace lo siguiente:

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

Así que aquí, obviamente, codifica explícitamente su cadena Unicode en utf-8 o lo usa codecs.openpara hacerlo de forma transparente.

Si solo usa cadenas de bytes, entonces no hay problemas:

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

Se complica más que esto porque cuando concatenas una cadena unicode y una cadena de bytes con el +operador, obtienes una cadena unicode. Es fácil que te muerda ese.

Tampoco codecs.openle gusta que se pasen cadenas de bytes con caracteres no 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)

El consejo sobre cadenas para entrada / salida es normalmente "convertir a Unicode lo antes posible y volver a cadenas de bytes lo más tarde posible". El uso le codecs.openpermite hacer esto último muy fácilmente.

Solo tenga cuidado de darle cadenas Unicode y no cadenas de bytes que pueden tener caracteres no ASCII.

Mandíbula79
fuente
¿Puede explicar su segundo ejemplo? Parece ser idéntico a su primer ejemplo, entonces, ¿por qué el resultado sería diferente?
Chris Johnson
Tenga en cuenta el uso de u''en el primer ejemplo. Esto significa que creé una cadena Unicode, no una cadena de bytes. Ésta es la diferencia entre los dos ejemplos. En el segundo ejemplo, estoy creando una cadena de bytes y escribir una de esas en un archivo está bien. Una cadena Unicode no está bien si está utilizando caracteres fuera de ASCII.
Mandible79
7

Cuando necesite abrir un archivo que tenga una determinada codificación, utilizará el codecsmódulo.

Geo
fuente
15
Supongo que todos los archivos de texto tienen cierta codificación, de alguna manera (:
cedbeu
5

codecs.open, supongo, es solo un remanente de los Python 2días en que el sistema abierto incorporado tenía una interfaz mucho más simple y menos capacidades. En Python 2, lo integrado openno acepta un argumento de codificación, por lo que si desea usar algo que no sea el modo binario o la codificación predeterminada, se suponía que debía usar codecs.open.

En Python 2.6, el módulo io vino en ayuda para simplificar un poco las cosas. Según la documentación oficial

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.

Dicho esto, el único uso que se me ocurre codecs.openen el escenario actual es la compatibilidad con versiones anteriores. En todos los demás escenarios (a menos que esté usando Python <2.6), es preferible usar io.open. También en Python 3.x io.openes lo mismo quebuilt-in open

Nota:

También hay una diferencia sintáctica entre codecs.openy 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)
aquí para aprender
fuente
No solo codecs.openy io.opendifieren en términos de sintaxis, devuelven objetos de diferente tipo. También codecs.opensiempre funciona con archivos en modo binario.
wombatonfire
4
  • Cuando desee cargar un archivo binario, use f = io.open(filename, 'b').

  • Para abrir un archivo de texto, utilícelo siempre f = io.open(filename, encoding='utf-8')con codificación explícita.

Sin embargo, en python 3open hace lo mismo que io.openy se puede usar en su lugar.

Nota: codecs.open está previsto que quede obsoleto y se sustituya por io.opendespués de su introducción en Python 2.6 . Solo lo usaría si el código necesita ser compatible con versiones anteriores de Python. Para obtener más información sobre códecs y Unicode en Python, consulte Unicode HOWTO .

wihlke
fuente
1. ¿Por qué no puedo abrir un archivo en modo binario con io.openo codecs.open? 2. codecs.openaún no está en desuso, lea la discusión en la página a la que vinculó.
wombatonfire
¡Buenos puntos! 1. Puede utilizar cualquiera de las dos, pero le aconsejaría de nuevo que no utilice codecs.open a menos que tenga python 2.5 o una versión anterior. 2. Actualicé mi respuesta para reflejar que la desaprobación no se produjo de inmediato, sino en el futuro.
wihlke
3

Cuando trabaja con archivos de texto y desea codificación y decodificación transparente en objetos Unicode.

Gato Plus Plus
fuente
0

Estaba en una situación para abrir un archivo .asm y procesar el archivo.

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

Sin muchos problemas puedo leer todo el archivo, ¿alguna sugerencia?

Gowtham Ch
fuente