2012-05-08 18 views
14

Cuando uso open() para abrir un archivo, no puedo escribir cadenas de unicode. Aprendí que necesito usar codecs y abrir el archivo con codificación Unicode (ver http://docs.python.org/howto/unicode.html#reading-and-writing-unicode-data).¿Cómo crear un archivo temporal con codificación Unicode?

Ahora necesito crear algunos archivos temporales. Traté de usar la biblioteca tempfile, pero no tiene ninguna opción de codificación. Cuando trato de escribir cualquier cadena Unicode en un archivo temporal con tempfile, falla:

#!/usr/bin/python2.6 
# -*- coding: utf-8 -*- 
import tempfile 
with tempfile.TemporaryFile() as fh: 
    fh.write(u"Hello World: ä") 
    fh.seek(0) 
    for line in fh: 
    print line 

¿Cómo puedo crear un archivo temporal con la codificación Unicode en Python?

Editar:

  1. estoy usando Linux y el mensaje de error que me sale de este código es:

    Traceback (most recent call last): 
        File "tmp_file.py", line 5, in <module> 
        fh.write(u"Hello World: ä") 
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 13: ordinal not in range(128) 
    
  2. Esto es sólo un ejemplo. En la práctica, intento escribir una cadena que devuelva alguna API.

Respuesta

16

de todos los demás respuestas son correctas, sólo quiero aclarar lo que está pasando:

La diferencia entre el 'foo' literal y el u'foo literal' es que el primero es una cadena de bytes y el segundo es el objeto Unicode

Primero, entienda que Unicode es el juego de caracteres. UTF-8 es la codificación. El objeto Unicode es el primero: es una cadena Unicode, no necesariamente una UTF-8. En su caso, la codificación de un literal de cadena será UTF-8, porque usted lo especificó en las primeras líneas del archivo.

Para obtener una cadena Unicode de una cadena de bytes, se llama al método .encode

>>>> u"ひらがな".encode("utf-8") == "ひらがな" 
True 

Del mismo modo, se podría llamar su string.encode en la llamada write y lograr el mismo efecto que sólo la eliminación de la u .

Si no especificó la codificación en la parte superior, por ejemplo, si estaba leyendo los datos Unicode de otro archivo, debería especificar en qué codificación estaba antes de llegar a una cadena de Python. Esto determinaría cómo se representaría en bytes (es decir, el tipo str).

El error que está recibiendo, entonces, es solo porque el módulo tempfile está esperando un objeto str. Esto no significa significa que no puede manejar unicode, solo que espera que pase en una cadena de bytes en lugar de un objeto Unicode, porque sin especificar una codificación, no sabría cómo escribirla en el archivo temporal.

+2

Sí. Por lo tanto, no es necesario abrir el archivo temporal con alguna opción Unicode mágica, es suficiente para escribir una cadena explícitamente codificada: 'fh.escribe (u'föo bār'.encode ('utf-8')) '. Reemplace 'utf-8' con 'utf-16' si la mayoría de sus personajes son CJK. – 9000

+1

@ 9000: tenga cuidado con este método si usa 'utf-16'. Si lo hace, tendrá que escribir todo el archivo de una vez, porque la codificación ('utf-16') también muestra la lista de materiales del archivo. Si tiene varias cadenas para escribir en el mismo archivo, la primera debe estar con .encode ('utf-16') y las siguientes con .encode ('utf-16-le') que no envía la lista de materiales. El uso de alguna opción mágica Unicode evitar este escollo. – kriss

+0

'" abc "' es una cadena Unicode en Python 3 o en presencia de 'from __future__ import unicode_literals'. – jfs

6

he descubierto una solución: crear un archivo temporal que no se elimina automáticamente con tempfile, cerrarlo y abrirlo de nuevo utilizando codecs:

#!/usr/bin/python2.6 
# -*- coding: utf-8 -*- 

import codecs 
import os 
import tempfile 

f = tempfile.NamedTemporaryFile(delete=False) 
filename = f.name 
f.close() 

with codecs.open(filename, 'w+b', encoding='utf-8') as fh: 
    fh.write(u"Hello World: ä") 
    fh.seek(0) 
    for line in fh: 
    print line 

os.unlink(filename) 
+0

Lo siento, pero esto no es óptimo. Ver la respuesta de @ spinning_plate y mi comentario al respecto; las cosas son más simples. – 9000

+0

@ 9000 No veo una respuesta de 'spinning_plate' aquí. – guettli

+0

@guettli: debe ser un tipo de error tipográfico; Debo haber querido decir la respuesta de 'dfb', actualmente la aceptada. – 9000

0

Caída de la U hecho su trabajo código para mí :

fh.write("Hello World: ä") 

Supongo que es porque ya es unicode.

+0

¿El archivo tiene la salida correcta? – dfb

+0

Sí, ejecutar el script en un linux box, sin el u, produce la salida correcta 'Hello World: ä' – John

+0

Sí, esto funciona ... En realidad, en mi programa real recibo la entrada de alguna API, y falla, así que no fue por "the u" en mi código. – dbarbosa

1

Está tratando de escribir un objeto Unicode (u"...") en el archivo temporal donde debe usar una cadena codificada ("..."). No tiene que pasar explícitamente un parámetro "encode=", porque ya ha indicado la codificación en la línea dos ("# -*- coding: utf-8 -*-"). Solo use fh.write("ä") en lugar de fh.write(u"ä") y debería estar bien.

+0

Sí, esto funciona, pero en realidad estoy tratando de escribir una cadena que devuelva alguna API, por lo que no hay '(u" ... ")' en mi código. He actualizado mi pregunta con esta información. Probé un ejemplo con dos archivos, y 'fh.write (other_file.f())' funciona o no, dependiendo del otro archivo que tenga la codificación o no. En mi código real, no tengo ningún control del código que está creando la cadena. – dbarbosa

6

tempfile.TemporaryFile tiene encoding option in Python 3:

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import tempfile 
with tempfile.TemporaryFile(mode='w+', encoding='utf-8') as fh: 
    fh.write("Hello World: ä") 
    fh.seek(0) 
    for line in fh: 
    print(line) 

Tenga en cuenta que ahora es necesario especificar el modo = 'w +' en lugar del modo binario predeterminado. También tenga en cuenta que los literales de cadena son implícitamente Unicode en Python 3, no existe el modificador u.

Si le pegan con Python 2.6, temporary files son siempre binario, y hay que codificar la cadena Unicode antes de escribirla en el archivo:

#!/usr/bin/python 
# -*- coding: utf-8 -*- 
import tempfile 
with tempfile.TemporaryFile() as fh: 
    fh.write(u"Hello World: ä".encode('utf-8')) 
    fh.seek(0) 
    for line in fh: 
    print line.decode('utf-8') 

Unicode especifica el conjunto de caracteres, no la codificación, por lo que en cualquier caso de que necesite una forma de especificar cómo codificar los caracteres Unicode!

+0

Es una buena idea decodificar también la cadena de 8 bits que se lee del archivo (en el ejemplo de Python 2), convirtiéndola en una cadena Unicode, antes de imprimir. (Arreglado.) –

4

Como estoy trabajando en un programa Python con objetos TemporaryFile que deben ejecutarse tanto en Python 2 como en Python 3, no me parece satisfactorio codificar manualmente todas las cadenas escritas como UTF-8 como sugieren las otras respuestas.

En cambio, he escrito la siguiente pequeña polyfill (porque no podía encontrar algo parecido en seis) para envolver un objeto de tipo fichero binario en un archivo como objeto UTF-8:

from __future__ import unicode_literals 
import sys 
import codecs 
if sys.hexversion < 0x03000000: 
    def uwriter(fp): 
     return codecs.getwriter('utf-8')(fp) 
else: 
    def uwriter(fp): 
     return fp 

se utiliza de la siguiente manera:

# encoding: utf-8 
from tempfile import NamedTemporaryFile 
with uwriter(NamedTemporaryFile(suffix='.txt', mode='w')) as fp: 
    fp.write('Hællo wörld!\n') 
0

Configuración sys como la codificación por defecto a UTF-8 se solucionará el problema de codificación

import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') #set to utf-8 by default this will solve the errors 

import tempfile 
with tempfile.TemporaryFile() as fh: 
    fh.write(u"Hello World: ä") 
    fh.seek(0) 
    for line in fh: 
    print line 
Cuestiones relacionadas