web-dev-qa-db-fra.com

Écrire dans un fichier UTF-8 en Python

Je suis vraiment confus avec le codecs.open function. Quand je fais:

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

Ça me donne l'erreur

UnicodeDecodeError: le codec 'ascii' ne peut pas décoder l'octet 0xef en position 0: l'ordinal n'est pas dans la plage (128)

Si je fais:

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

Ça fonctionne bien.

Question pourquoi la première méthode échoue-t-elle? Et comment puis-je insérer le bébé?

Si la deuxième méthode est la bonne façon de le faire, quel est l'intérêt d'utiliser codecs.open(filename, "w", "utf-8")?

180
John Jiang

Je crois que le problème est que codecs.BOM_UTF8 est une chaîne d'octets, pas une chaîne Unicode. Je suppose que le gestionnaire de fichiers essaie de deviner ce que vous voulez vraiment dire en vous basant sur "Je suis censé écrire Unicode en tant que texte codé en UTF-8, mais vous m'avez attribué une chaîne d'octets!"

Essayez d’écrire directement la chaîne Unicode correspondant à la marque d’octet (c.-à-d. Unicode U + FEFF), de sorte que le fichier ne le code qu’en UTF-8:

_import codecs

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

(Cela semble donner la bonne réponse - un fichier avec les octets EF BB BF.)

EDIT: S. Lott's suggestion d'utiliser "utf-8-sig" car l'encodage est meilleur que l'écriture explicite de la nomenclature vous-même, mais je laisserai cette réponse ici car elle explique ce qui se passait mal avant.

257
Jon Skeet

Lisez ce qui suit: http://docs.python.org/library/codecs.html#module-encodings.utf_8_sig

Faire ceci

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

Le fichier résultant est UTF-8 avec la nomenclature attendue.

166
S.Lott

@ S-Lott donne la bonne procédure, mais en développant les problèmes Unicode , le Python l'interprète peut fournir plus d'informations.

Jon Skeet a raison (inhabituel) à propos du module codecs - il contient des chaînes d'octets:

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

En choisissant un autre nit, le BOM a un nom standard Unicode et peut être entré de la manière suivante:

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

Il est également accessible via unicodedata :

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

J'utilise la commande file * nix pour convertir un fichier de jeu de caractères inconnu en un fichier 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()
8
Ricardo