2009-06-01 7 views
145

Je suis vraiment confondu avec le codecs.open function. Quand je fais:Écrire au fichier UTF-8 en Python

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

Il me donne l'erreur

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

Si je fais:

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

Il fonctionne très bien.

Question Pourquoi la première méthode échoue-t-elle? Et comment puis-je insérer le bom? Si la deuxième méthode est la bonne façon de le faire, à quoi bon utiliser codecs.open(filename, "w", "utf-8")?

+40

Ne pas utiliser une nomenclature en UTF-8. *** S'il vous plaît. *** – tchrist

+6

@tchrist Huh?Pourquoi pas? –

+6

@SalmanPK BOM n'est pas nécessaire dans UTF-8 et ajoute seulement de la complexité (par exemple, vous ne pouvez pas simplement concaténer les fichiers BOM et obtenir un texte valide). Voir [cette Q & A] (http://stackoverflow.com/questions/2223882/whats-different-between-utf-8-and-utf-8-without-bom); ne manquez pas le grand commentaire sous Q –

Répondre

213

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

Essayez d'écrire la chaîne Unicode pour la marque d'ordre d'octet (ie Unicode U + FEFF) directement, de sorte que le fichier code juste que 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 des octets EF BB BF.)

EDIT: suggestion de S. Lott d'utiliser "utf-8-sig" comme l'encodage est une meilleure que d'écrire explicitement la nomenclature vous-même, mais je vais laisser cette réponse ici comme il explique ce qui n'allait pas avant.

+0

Merci beaucoup pour cela, a certainement fait les choses plus claires –

+0

Attention: ouvrir et ouvrir n'est pas la même chose. Si vous faites "depuis l'importation des codecs ouverts", ce ne sera pas la même chose que vous tapez simplement "open". – Shiki

+0

vous pouvez également utiliser codecs.open ('test.txt', 'w', 'utf-8-sig') à la place –

150

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

Est-ce que ce

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 prévue.

+1

Merci. Cela a fonctionné (Windows 7 x64, Python 2.7.5 x64). Cette solution fonctionne bien lorsque vous ouvrez le fichier en mode "a" (append). –

+0

'importe les codecs 'en premier. – KrisWebDev

+0

Cela n'a pas fonctionné pour moi, Python 3 sur Windows. Je devais le faire à la place avec open (nom_fichier, 'wb') comme bomfile: bomfile.write (codecs.BOM_UTF8) puis rouvrir le fichier pour ajouter. –

11

@ S-Lott donne la bonne procédure, mais en expansion sur les questions Unicode, l'interpréteur Python peut fournir plus d'idées.

Jon Skeet est juste (inhabituel) sur le module codecs - il contient des chaînes d'octets:

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

Choisir une autre petite bête, le BOM a un nom standard Unicode, et il peut être entré comme:

>>> 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' 
>>> 
+0

J'ai essayé d'enrichir votre réponse tout en gardant votre esprit. – tzot

5

J'utilise le fichier * commande nix pour convertir un fichier charset inconnu dans 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() 
+0

De nos jours, vous pouvez également utiliser [chardet] (https://pypi.python.org/pypi/chardet). –

+0

Utilisez '# coding: utf8' au lieu de' # - * - coding: utf-8 - * - 'qui est beaucoup plus facile à retenir. – show0k

Questions connexes