Escribir en el archivo UTF-8 en Python

Estoy realmente confundido con la codecs.open function . Cuando lo hago:

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

Me da el error

UnicodeDecodeError: el codec ‘ascii’ no puede decodificar el byte 0xef en la posición 0: ordinal no está en el rango (128)

Si lo hago:

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

Funciona bien.

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

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

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

Intente escribir la cadena Unicode para la marca de orden de bytes (es decir, Unicode U + FEFF) directamente, de modo que el archivo solo lo codifique 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).

EDICIÓN: la sugerencia de S. Lott de usar “utf-8-sig” ya que la encoding es mejor que escribir explícitamente la lista de materiales, pero dejaré esta respuesta aquí, ya que explica lo que estuvo mal antes.

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

Hacer 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 lista de materiales esperada.

@ S-Lott proporciona el procedimiento correcto, pero al expandir los problemas de Unicode , el intérprete de Python puede proporcionar más información.

Jon Skeet tiene razón (no es habitual) sobre el módulo de codecs ; contiene cadenas de bytes:

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

Al elegir otra liendre, la BOM tiene un nombre estándar de Unicode y se puede ingresar 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' >>> 

Uso el comando * nix del archivo para convertir un archivo de juego de caracteres desconocido 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()