utf8 - Lectura y escritura de Unicode(UTF-8) en archivos en Python
manejo de archivos binarios en python (13)
Ahora todo lo que necesitas en Python3 está open(Filename, ''r'', encoding=''utf-8'')
[Editar en 2016-02-10 para aclaración solicitada]
Python3 agregó el parámetro de codificación a su función abierta. La siguiente información sobre la función abierta se recopila 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 solo debe ser usado en el modo de texto. La codificación predeterminada depende de la plataforma (lo que locale.getpreferredencoding() ), pero se puede usar cualquier codificación de texto compatible con Python. Consulte el módulo de codecs para la lista de codificaciones compatibles.
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).
Tengo una falla cerebral al entender leer y escribir 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 Capit/xc3/xa1n
en 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é es lo que no entiendo aquí? Claramente hay un poco de magia vital (o sentido común) que me estoy perdiendo. ¿Qué se escribe en los archivos de texto para obtener las conversiones adecuadas?
Lo que realmente estoy fallando al asimilar aquí es a qué se refiere la representación de UTF-8, si realmente no puedes hacer que Python lo reconozca, cuando viene de fuera. Tal vez debería simplemente JSON volcar 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 ingrese desde 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''
Bueno, su editor de texto favorito no se da cuenta de que se supone que /xc3/xa1
son literales de caracteres, pero los interpreta como texto. Es por eso que obtiene la doble barra diagonal inversa en la última línea: ahora es una barra invertida real + xc3
, etc. en su archivo.
Si desea leer y escribir archivos codificados en Python, utilice mejor el módulo de codecs .
Pegar texto entre el terminal y las aplicaciones es difícil, porque no sabes qué programa interpretará tu texto con qué codificación. Podrías probar lo siguiente:
>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán
Luego pegue esta cadena en su editor y asegúrese de que la almacene usando Latin-1. Bajo el supuesto de que el portapapeles no distorsiona la cadena, el viaje de ida y vuelta debería funcionar.
En la notación
u''Capit/xe1n/n''
el "/ xe1" representa solo un byte. "/ x" te dice que "e1" está en hexadecimal. Cuando escribes
Capit/xc3/xa1n
en su archivo tiene "/ xc3" en él. Son 4 bytes y en tu código los lees todos. Puedes ver esto cuando los muestres:
>>> open(''f2'').read()
''Capit//xc3//xa1n/n''
Puedes ver que la barra invertida se escapa mediante una barra invertida. Así que tienes cuatro bytes en tu cadena: "/", "x", "c" y "3".
Editar:
Como otros lo señalaron en sus respuestas, solo debe ingresar los caracteres en el editor y su editor debe manejar la conversión a UTF-8 y guardarla.
Si realmente tiene una cadena en este formato, puede usar el códec string_escape
para decodificarla en una cadena normal:
In [15]: print ''Capit//xc3//xa1n/n''.decode(''string_escape'')
Capitán
El resultado es una cadena codificada en UTF-8 donde el carácter acentuado está representado por los dos bytes que se escribieron //xc3//xa1
en la cadena original. Si desea tener una cadena Unicode, debe decodificar nuevamente con UTF-8.
Para su edición: no tiene UTF-8 en su archivo. Para ver realmente cómo se vería:
s = u''Capit/xe1n/n''
sutf8 = s.encode(''UTF-8'')
open(''utf-8.out'', ''w'').write(sutf8)
Compare el contenido del archivo utf-8.out
con el contenido del archivo que guardó con su editor.
En lugar de meterse con los métodos de codificación y decodificación, me resulta más fácil especificar la codificación al abrir el archivo. El módulo io
(agregado en Python 2.6) proporciona una función io.open
, que tiene un parámetro de codificación.
Utilice el método abierto del módulo io
.
>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")
Luego, después de llamar a la función read () de f, se devuelve un objeto Unicode codificado.
>>>f.read()
u''Capit/xe1l/n/n''
Tenga en cuenta que en Python 3, la función io.open
es un alias para la función de open
incorporada. La función abierta incorporada solo admite el argumento de codificación en Python 3, no Python 2.
Editar: Anteriormente esta respuesta recomendaba el módulo de codecs . El módulo de códecs puede causar problemas al mezclar read()
y readline()
, por lo que esta respuesta ahora recomienda el módulo io lugar.
Utilice el método abierto del módulo codecs.
>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")
Luego, después de llamar a la función read () de f, se devuelve un objeto Unicode codificado.
>>>f.read()
u''Capit/xe1l/n/n''
Si conoce la codificación de un archivo, usar el paquete de codecs será mucho menos confuso.
Consulte http://docs.python.org/library/codecs.html#codecs.open
En realidad, esto me funcionó 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)
Entonces, he encontrado una solución para lo que estoy buscando, que es:
print open(''f2'').read().decode(''string-escape'').decode("utf-8")
Hay algunos codecs inusuales que son útiles aquí. Esta lectura en particular le permite a uno tomar representaciones UTF-8 desde Python, copiarlas en un archivo ASCII y leerlas en Unicode. Bajo la decodificación "string-escape", las barras diagonales no se duplicarán.
Esto permite el tipo de viaje de ida y vuelta que estaba imaginando.
Estaba intentando analizar iCal usando Python 2.7.9:
Desde icalendar Import Calendar
Pero yo estaba recibiendo:
Traceback (most recent call last):
File "ical.py", line 92, in parse
print "{}".format(e[attr])
UnicodeEncodeError: ''ascii'' codec can''t encode character u''/xe1'' in position 7: ordinal not in range(128)
y fue arreglado con solo:
print "{}".format(e[attr].encode("utf-8"))
(Ahora puede imprimir liké á böss.)
La secuencia / x .. es algo que es específico de Python. No es una secuencia de escape de bytes universal.
La forma en que se ingresa en el código no ASCII codificado en UTF-8 depende de su sistema operativo o su editor. Así es como lo haces en Windows . Para que OS X ingrese a con un acento agudo, simplemente puede presionar la opción + E , luego A , y casi todos los editores de texto en OS X son compatibles con UTF-8.
Para leer una cadena Unicode y luego enviarla a HTML, hice esto:
fileline.decode("utf-8").encode(''ascii'', ''xmlcharrefreplace'')
Útil para los servidores http potenciados por Python.
Se ha topado con el problema general de las codificaciones: ¿Cómo puedo saber en qué codificación se encuentra un archivo?
Respuesta: No puedes a menos que el formato de archivo lo proporcione. XML, por ejemplo, comienza con:
<?xml encoding="utf-8"?>
Este encabezado se eligió cuidadosamente para que se pueda leer sin importar la codificación. En su caso, no hay tal sugerencia, por lo tanto, ni su editor ni Python tienen idea de lo que está sucediendo. Por lo tanto, debe usar el módulo de codecs
y codecs.open(path,mode,encoding)
que proporciona el bit faltante en Python.
En cuanto a su editor, debe verificar si ofrece alguna forma de establecer la codificación de un archivo.
El objetivo de UTF-8 es poder codificar caracteres de 21 bits (Unicode) como un flujo de datos de 8 bits (porque eso es lo único que pueden manejar todas las computadoras del mundo). Pero como la mayoría de los sistemas operativos son anteriores a la era Unicode, no tienen herramientas adecuadas para adjuntar la información de codificación a los archivos en el disco duro.
El siguiente tema es la representación en Python. Esto se explica perfectamente en el comentario de heikogerlach . Debe comprender que su consola solo puede mostrar ASCII. Para mostrar Unicode o cualquier cosa> = charcode 128, debe usar algún medio de escape. En su editor, no debe escribir la cadena de visualización con escape, sino lo que significa la cadena (en este caso, debe ingresar la diéresis y guardar el archivo).
Dicho esto, puedes usar la función eval () de Python para convertir una cadena escapada en una cadena:
>>> x = eval("''Capit//xc3//xa1n//n''")
>>> x
''Capit/xc3/xa1n/n''
>>> x[5]
''/xc3''
>>> len(x[5])
1
Como puede ver, la cadena "/ xc3" se ha convertido en un solo carácter. Esta es ahora una cadena de 8 bits codificada en UTF-8. Para obtener Unicode:
>>> x.decode(''utf-8'')
u''Capit/xe1n/n''
preguntó: Creo que faltan algunas piezas aquí: el archivo f2 contiene: hex:
0000000: 4361 7069 745c 7863 335c 7861 316e Capit/xc3/xa1n
codecs.open(''f2'',''rb'', ''utf-8'')
, por ejemplo, los lee todos en caracteres separados (esperado) ¿Hay alguna forma de escribir en un archivo en ASCII que funcione?
Respuesta: Eso depende de lo que quieras decir. ASCII no puede representar caracteres> 127. Así que necesita alguna forma de decir "los siguientes caracteres significan algo especial", que es lo que hace la secuencia "/ x". Dice: Los siguientes dos caracteres son el código de un solo carácter. "/ u" hace lo mismo usando cuatro caracteres para codificar Unicode hasta 0xFFFF (65535).
Por lo tanto, no puede escribir directamente Unicode en ASCII (porque ASCII simplemente no contiene los mismos caracteres). Puede escribirlo como cadenas de escape (como en f2); en este caso, el archivo se puede representar como ASCII. O puede escribirlo como UTF-8, en cuyo caso, necesita un flujo seguro de 8 bits.
Su solución con decode(''string-escape'')
funciona, pero debe tener en cuenta la cantidad de memoria que usa: Tres veces la cantidad de uso de codecs.open()
.
Recuerde que un archivo es solo una secuencia de bytes con 8 bits. Ni los bits ni los bytes tienen un significado. Eres tú quien dice "65 significa ''A''". Como /xc3/xa1
debería convertirse en "à" pero la computadora no tiene medios para saberlo, debe indicarlo especificando la codificación que se usó al escribir el archivo.
También puede mejorar la función original open()
para trabajar con archivos Unicode reemplazándola en su lugar, utilizando la función partial
. La belleza de esta solución es que no necesita cambiar ningún código antiguo. Es transparente
import codecs
import functools
open = functools.partial(codecs.open, encoding=''utf-8'')
a excepción de codecs.open()
, se puede usar io.open()
para trabajar con Python2 o Python3 para leer / escribir un archivo Unicode
ejemplo
import io
text = u''á''
encoding = ''utf8''
with io.open(''data.txt'', ''w'', encoding=encoding, newline=''/n'') as fout:
fout.write(text)
with io.open(''data.txt'', ''r'', encoding=encoding, newline=''/n'') as fin:
text2 = fin.read()
assert text == text2
# -*- encoding: utf-8 -*-
# converting a unknown formatting file in utf-8
import codecs
import commands
file_location = "jumper.sub"
file_encoding = commands.getoutput(''file -b --mime-encoding %s'' % file_location)
file_stream = codecs.open(file_location, ''r'', file_encoding)
file_output = codecs.open(file_location+"b", ''w'', ''utf-8'')
for l in file_stream:
file_output.write(l)
file_stream.close()
file_output.close()