Unicode (UTF-8) lectura y escritura en archivos en Python

Resuelto Gregg Lind asked hace 15 años • 15 respuestas

Tengo cierta insuficiencia cerebral para comprender la lectura y escritura de texto en un archivo (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("u'Capit\xe1n'", "'Capit\xc3\xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

Entonces escribo Capit\xc3\xa1nen mi editor favorito, en el archivo f2.

Entonces:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'

¿Qué no estoy entendiendo aquí? Es evidente que me falta algo vital de magia (o buen sentido). ¿Qué se escribe en archivos de texto para obtener conversiones adecuadas?

Lo que realmente no entiendo aquí es cuál es el objetivo de la representación UTF-8, si realmente no puedes lograr que Python la reconozca, cuando viene desde afuera. ¡Quizás debería simplemente volcar JSON la cadena y usarla en su lugar, ya que tiene una representación asciiable! Más concretamente, ¿existe una representación ASCII de este objeto Unicode que Python reconocerá y decodificará cuando provenga de un archivo? Si es así, ¿cómo lo consigo?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
Gregg Lind avatar Jan 29 '09 22:01 Gregg Lind
Aceptado

En lugar de meterse con .encodey .decode, especifique la codificación al abrir el archivo. El iomódulo , agregado en Python 2.6, proporciona una io.openfunción que permite especificar el archivo encoding.

Suponiendo que el archivo esté codificado en UTF-8, podemos usar:

>>> import io
>>> f = io.open("test", mode="r", encoding="utf-8")

Luego f.readdevuelve un objeto Unicode decodificado:

>>> f.read()
u'Capit\xe1l\n\n'

En 3.x, la io.openfunción es un alias para la función incorporada open, que admite el encodingargumento (no es así en 2.x).

También podemos usar opendesde el codecsmódulo de biblioteca estándar :

>>> import codecs
>>> f = codecs.open("test", "r", "utf-8")
>>> f.read()
u'Capit\xe1l\n\n'

Sin embargo, tenga en cuenta que esto puede causar problemas al mezclar read()yreadline() .

Tim Swast avatar May 10 '2009 00:05 Tim Swast

En la notación u'Capit\xe1n\n'(debe estar solo 'Capit\xe1n\n'en 3.x y debe estar en 3.0 y 3.1), \xe1representa solo un carácter. \xes una secuencia de escape, lo que indica que e1está en hexadecimal.

Escribir Capit\xc3\xa1nen el archivo en un editor de texto significa que en realidad contiene \xc3\xa1. Son 8 bytes y el código los lee todos. Podemos ver esto mostrando el resultado:

# Python 3.x - reading the file as bytes rather than text,
# to ensure we see the raw data
>>> open('f2', 'rb').read()
b'Capit\\xc3\\xa1n\n'

# Python 2.x
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

En su lugar, simplemente ingrese caracteres como áen el editor, que luego debería manejar la conversión a UTF-8 y guardarla.

En 2.x, una cadena que realmente contiene estas secuencias de escape de barra invertida se puede decodificar usando el string_escapecódec:

# Python 2.x
>>> print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

El resultado es un strcódigo que está codificado en UTF-8 donde el carácter acentuado está representado por los dos bytes que se escribieron \\xc3\\xa1en la cadena original. Para obtener un unicoderesultado, decodifique nuevamente con UTF-8.

En 3.x, el string_escapecódec se reemplaza por unicode_escapey se aplica estrictamente que solo podemos encodeir de a stra bytesy decodede bytesa str. unicode_escapenecesita comenzar con a bytespara procesar las secuencias de escape (al revés, las agrega ); y luego tratará el resultado \xc3y \xa1como escapes de caracteres en lugar de escapes de bytes . Como resultado, tenemos que trabajar un poco más:

# Python 3.x
>>> 'Capit\\xc3\\xa1n\n'.encode('ascii').decode('unicode_escape').encode('latin-1').decode('utf-8')
'Capitán\n'
 avatar Jan 29 '2009 15:01

Ahora todo lo que necesitas en Python3 esopen(Filename, 'r', encoding='utf-8')

[Editar el 10 de febrero de 2016 para solicitar aclaración]

Python3 agregó el parámetro de codificación a su función abierta. La siguiente información sobre la función abierta se recopila desde aquí: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

Codificación es el nombre de la codificación utilizada para decodificar o codificar el archivo. Esto sólo debe usarse en modo texto. La codificación predeterminada depende de la plataforma (lo que devuelva locale.getpreferredencoding() ), pero se puede utilizar cualquier codificación de texto compatible con Python. Consulte el módulo de códecs para obtener la lista de codificaciones admitidas.

Entonces, al agregar encoding='utf-8'como parámetro a la función de apertura, la lectura y escritura del archivo se realiza como utf8 (que ahora también es la codificación predeterminada de todo lo que se hace en Python).

Dakusan avatar Feb 10 '2016 16:02 Dakusan

Esto funciona para leer un archivo con codificación UTF-8 en Python 3.2:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
Sina avatar Aug 19 '2014 08:08 Sina

Entonces, encontré una solución para lo que estoy buscando, que es:

print open('f2').read().decode('string-escape').decode("utf-8")

Hay algunos códecs inusuales que resultan útiles aquí. Esta lectura particular permite tomar representaciones UTF-8 desde Python, copiarlas en un archivo ASCII y leerlas en Unicode. Bajo la decodificación "string-escape", las barras no se duplicarán.

Esto permite el tipo de viaje de ida y vuelta que estaba imaginando.

Gregg Lind avatar Jan 29 '2009 20:01 Gregg Lind