open - python decode unicode
Diferencia entre open y codecs.open en Python (5)
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 preferible open
codecs.open
?
Cuando necesite abrir un archivo que tenga cierta codificación, usará el módulo de codecs
.
Cuando trabajas con archivos de texto y quieres codificación y decodificación transparentes en objetos Unicode.
Desde Python 2.6, una buena práctica es usar io.open()
, que también toma un argumento de encoding
, como el ahora obsoleto codecs.open()
. En Python 3, io.open
es un alias para el open()
incorporado. Así que io.open()
funciona en Python 2.6 y todas las versiones posteriores, incluyendo Python 3.4. Ver documentos: http://docs.python.org/3.4/library/io.html
Ahora, para la pregunta original: al leer texto (incluidos "texto sin formato", HTML, XML y JSON) en Python 2 siempre debe usar io.open()
con una codificación explícita, o open()
con una codificación explícita en Python 3. Si lo hace, significa que se decodifica Unicode correctamente, o se obtiene un error desde el principio, por lo que es mucho más fácil de depurar.
Pure ASCII "texto sin formato" es un mito del pasado lejano. El texto correcto en inglés usa comillas, guiones, viñetas, € (signos en euros) e incluso diaéresis (¨). ¡No seas ingenuo! (¡Y no olvidemos el patrón de diseño de Fachada!)
Como ASCII puro no es una opción real, open()
sin una codificación explícita solo es útil para leer archivos binarios .
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, por ejemplo, tienes una cadena Unicode y haces 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)
Entonces, obviamente, aquí codificas explícitamente tu cadena Unicode en utf-8 o usas codecs.open
para hacerlo de forma transparente.
Si solo usa cadenas de bytes, entonces no hay problemas:
>>> example = ''Μου αρέσει Ελληνικά''
>>> open(''sample.txt'', ''w'').write(example)
>>>
Se vuelve más complicado que esto porque cuando concatenas una cadena unicode y bytestring con el operador +
obtienes una cadena Unicode. Fácil de ser mordido por ese.
Además codecs.open
no le gusta las cadenas de bytes con caracteres no ASCII que se pasan en:
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 las cadenas de entrada / salida es normalmente "convertir a unicode lo más temprano posible y volver a las cadenas de bytes lo más tarde posible". Usar codecs.open
te permite hacer esto último muy fácilmente.
Solo tenga cuidado de que le esté dando cadenas unicode y no cadenas de bytes que pueden tener caracteres que no sean ASCII.
Personalmente, siempre uso codecs.open
menos que haya una clara necesidad identificada de usar open
**. La razón es que ha habido tantas ocasiones en que me han picado al tener entrada de utf-8 en mis programas. "Oh, yo 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 el inverso no es verdadero. Y en esos casos, cuando realmente sé que la entrada es ASCII, sigo haciendo codecs.open
ya que soy un firme creyente en "explícito es mejor que implícito" .
** - en Python 2.x, como dice el comentario sobre la pregunta en Python 3 open
reemplaza codecs.open