2009-06-01 13 views
145

Estoy realmente confundido con el codecs.open function. Cuando hago:Escribir en el archivo UTF-8 en Python

file = codecs.open("temp", "w", "utf-8") 
file.write(codecs.BOM_UTF8) 
file.close() 

Me da el error

UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 0: ordinal not in range(128)

Si hago:

file = open("temp", "w") 
file.write(codecs.BOM_UTF8) 
file.close() 

Trabaja muy bien.

Pregunta ¿por qué falla el primer método? ¿Y cómo inserto el bom?

Si el segundo método es la forma correcta de hacerlo, ¿qué sentido tiene usar codecs.open(filename, "w", "utf-8")?

+40

No utilice una lista de materiales en UTF-8. *** Por favor. *** – tchrist

+6

@tchrist Huh?Por qué no? –

+6

@SalmanPK La BOM no es necesaria en UTF-8 y solo agrega complejidad (por ejemplo, no puede concatenar los archivos de BOM y obtener un texto válido). Ver [este Q & A] (http://stackoverflow.com/questions/2223882/whats-different-between-utf-8-and-utf-8-without-bom); no te pierdas el gran comentario bajo Q –

Respuesta

213

Creo que el problema es que codecs.BOM_UTF8 es una cadena de bytes, no una cadena Unicode. Sospecho que el manejador de archivos está tratando de adivinar lo que realmente quieres decir, basado en "¡Estoy destinado a escribir Unicode como texto codificado en UTF-8, pero me has dado una cadena de bytes!"

Trate de escribir la cadena Unicode para la marca de orden de bytes (es decir Unicode U + FEFF) directamente, por lo que el archivo simplemente codifica que como UTF-8:

import codecs 

file = codecs.open("lol", "w", "utf-8") 
file.write(u'\ufeff') 
file.close() 

(Eso parece dar la respuesta correcta - un archivo con bytes EF BB BF.)

EDIT: S. Lott suggestion de utilizar "utf-8-sig", ya que la codificación es mejor que escribir la BOM explícitamente, pero voy a dejar esta respuesta aquí, ya que explica lo que estaba pasando mal antes.

+0

Muchas gracias por esto, definitivamente ha aclarado las cosas –

+0

Advertencia: abrir y abrir no es lo mismo. Si lo haces "desde la importación de códecs abiertos", NO será lo mismo que simplemente escribirás "abrir". – Shiki

+0

también puede usar codecs.open ('test.txt', 'w', 'utf-8-sig') en su lugar –

150

leer lo siguiente: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Haga esto

with codecs.open("test_output", "w", "utf-8-sig") as temp: 
    temp.write("hi mom\n") 
    temp.write(u"This has ♭") 

El archivo resultante es UTF-8 con la esperada lista de materiales.

+1

Gracias. Eso funcionó (Windows 7 x64, Python 2.7.5 x64). Esta solución funciona bien cuando abre el archivo en el modo "a" (adjuntar). –

+0

'import codecs' primero. – KrisWebDev

+0

Esto no funcionó para mí, Python 3 en Windows. Tuve que hacer esto en lugar de abrir (file_name, 'wb') como bomfile: bomfile.write (codecs.BOM_UTF8) y luego volver a abrir el archivo para anexar. –

11

@ S-Lott da el procedimiento correcto, pero en expansión en los Unicode cuestiones, el Python intérprete le puede dar más ideas.

Jon Skeet es correcto (inusual) sobre el módulo codecs - que contiene cadenas de bytes:

>>> import codecs 
>>> codecs.BOM 
'\xff\xfe' 
>>> codecs.BOM_UTF8 
'\xef\xbb\xbf' 
>>> 

Recogiendo otra nit, la BOM tiene un estándar nombre Unicode, y puede ser introducido como:

>>> bom= u"\N{ZERO WIDTH NO-BREAK SPACE}" 
>>> bom 
u'\ufeff' 

también es accesible a través de unicodedata:

>>> import unicodedata 
>>> unicodedata.lookup('ZERO WIDTH NO-BREAK SPACE') 
u'\ufeff' 
>>> 
+0

Traté de enriquecer tu respuesta manteniendo tu espíritu. – tzot

5

uso el comando de archivo * nix para convertir un archivo de conjunto de caracteres desconocidos en un archivo UTF-8

# -*- 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() 
+0

Hoy en día, también puede usar [chardet] (https://pypi.python.org/pypi/chardet). –

+0

Use '#coording: utf8' en lugar de' # - * - coding: utf-8 - * - ', que es mucho más fácil de recordar. – show0k

Cuestiones relacionadas