Unicode (UTF-8) lire et écrire dans des fichiers en Python

j'ai une défaillance cérébrale dans la compréhension de la lecture et de l'écriture de texte dans un fichier (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capitxe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("u'Capitxe1n'", "'Capitxc3xa1n'")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capitxc3xa1nn'

alors je tape Capitxc3xa1n dans mon éditeur préféré, dans le fichier f2.

puis:

>>> open('f1').read()
'Capitxc3xa1nn'
>>> open('f2').read()
'Capitxc3xa1nn'
>>> open('f1').read().decode('utf8')
u'Capitxe1nn'
>>> open('f2').read().decode('utf8')
u'Capitxc3xa1nn'

Qu'est-ce que je ne comprends pas ici? Il est clair qu'il y a une part vitale de magie (ou de bon sens) qui Je suis absent. Qu'est-ce qu'on tape dans les fichiers texte pour obtenir des conversions appropriées?

ce que je ne suis vraiment pas capable de grok ici, c'est ce que le but de la représentation UTF-8 est, si vous ne pouvez pas réellement faire en sorte que Python le reconnaisse, quand il vient de l'extérieur. Peut-être que je devrais juste jeter la chaîne, et l'utiliser à la place, puisque cela a une représentation asciable! Plus précisément, y a-t-il une représentation ASCII de cet objet Unicode que Python reconnaîtra et décodera, quand en provenance d'un fichier? Si oui, comment puis-je l'obtenir?

>>> print simplejson.dumps(ss)
'"Capitu00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capitxe1n'
256
demandé sur Peter Mortensen 2009-01-29 18:01:15

13 réponses

dans la notation

u'Capit\xe1n\n'

le "\xe1 " ne représente qu'un octet. "\x" vous dit que "e1" est en hexadécimal. Quand vous écrivez

Capit\xc3\xa1n

dans votre fichier "\xc3". Ce sont 4 octets et dans votre code vous les lisez tous. Vous pouvez le voir quand vous les affichez:

>>> open('f2').read()
'Capit\xc3\xa1n\n'

vous pouvez voir que le backslash est échappé par un backslash. Donc, vous avez quatre octets dans votre chaîne: "\", "x", "c" et "3".

Edit:

comme d'autres l'ont souligné dans leurs réponses, vous devez simplement entrer les caractères dans l'éditeur et votre éditeur doit ensuite gérer la conversion en UTF-8 et de le sauvegarder.

si vous avez réellement une chaîne dans ce format, vous pouvez utiliser le codec string_escape pour la décoder en une chaîne normale:

In [15]: print 'Capit\xc3\xa1n\n'.decode('string_escape')
Capitán

le résultat est une chaîne qui est encodée en UTF-8 où le caractère accentué est représenté par les deux octets qui ont été écrits \xc3\xa1 dans la chaîne originale. Si vous voulez avoir une chaîne unicode vous devez décoder à nouveau avec UTF-8.

À votre edit: vous n'avez pas l'UTF-8 dans votre fichier. Pour voir à quoi ça ressemblerait:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

comparez le contenu du fichier utf-8.out au contenu du fichier que vous avez sauvegardé avec votre éditeur.

90
répondu 2009-01-29 18:44:41

plutôt que de gâcher les méthodes d'encodage et de décodage, je trouve plus facile de spécifier l'encodage lors de l'ouverture du fichier. Le io module (ajouté en python 2.6) fournit une fonction io.open , qui a un paramètre d'encodage.

utiliser la méthode ouverte du module io .

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

puis après avoir appelé la fonction read() de f, un objet Unicode encodé est retourné.

>>>f.read()
u'Capit\xe1l\n\n'

notez que dans Python 3, la fonction io.open est un alias pour la fonction intégrée open . La fonction ouverte intégrée supporte uniquement l'argument d'encodage en Python 3, Pas En Python 2.

modifier: auparavant cette réponse recommandé le codecs module. Le module codecs peut causer des problèmes lors du mélange read() et readline() , de sorte que cette réponse recommande maintenant la io module à la place.

utilisez la méthode ouverte du module codecs.

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

puis après avoir appelé la fonction read() de f, un objet Unicode encodé est retourné.

>>>f.read()
u'Capit\xe1l\n\n'

si vous connaissez l'encodage d'un fichier, l'utilisation du paquet codecs va être beaucoup moins déroutante.

voir http://docs.python.org/library/codecs.html#codecs.open

612
répondu Tim Swast 2018-09-03 22:16:04

maintenant tout ce dont vous avez besoin en Python3 est open(Filename, 'r', encoding='utf-8')

[Modifier sur 2016-02-10 pour demandé des éclaircissements]

Python3 a ajouté le paramètre encodant à sa fonction ouverte. Les informations suivantes sur la fonction ouverte sont rassemblées ici: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

encodage est le nom du codage utilisé pour décoder ou encoder fichier. Ceci ne doit être utilisé qu'en mode texte. Le codage par défaut est dépendant de la plate-forme (quel que soit le lieu .getpreferredencoding () returns), mais n'importe quel texte encodant supporté par Python peut être utilisé. Voir le module codecs pour la liste des encodages pris en charge.

ainsi en ajoutant encoding='utf-8' comme paramètre à l'open fonction, la lecture et l'écriture du fichier se font en utf8 (qui est aussi maintenant l'encodage par défaut de tout ce qui se fait en Python).)

20
répondu Dakusan 2018-03-04 08:20:30

donc, j'ai trouvé une solution pour ce que je cherche, qui est:

print open('f2').read().decode('string-escape').decode("utf-8")

il y a quelques codecs inhabituels qui sont utiles ici. Cette lecture particulière permet de prendre des représentations UTF-8 à partir de Python, de les copier dans un fichier ASCII, et de les faire lire en Unicode. Sous le décodage "string-escape", les slashes ne seront pas doublés.

cela permet le genre de voyage aller-retour que j'imaginais.

16
répondu Gregg Lind 2017-01-04 18:37:29

en fait, cela a fonctionné pour moi pour lire un fichier avec encodage UTF-8 en Python 3.2:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)
13
répondu Sina 2017-01-04 18:43:01
# -*- 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()
12
répondu Ricardo 2012-02-08 20:24:46

pour lire dans une chaîne Unicode et ensuite envoyer au HTML, j'ai fait ceci:

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

utile pour les serveurs http alimentés par python.

5
répondu praj 2014-09-18 14:38:14

sauf pour codecs.open() , on peut utiliser io.open() pour travailler avec Python2 ou Python3 pour lire / Écrire un fichier unicode

exemple

import io

text = u'á'
encoding = 'utf8'

with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
    fout.write(text)

with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
    text2 = fin.read()

assert text == text2
5
répondu Ryan 2017-07-18 01:32:24

Eh bien, votre éditeur de texte préféré ne se rend pas compte que \xc3\xa1 sont censés être des caractères littéraux, mais il les interprète comme du texte. C'est pourquoi vous obtenez le double backslash dans la dernière ligne -- c'est maintenant un vrai backslash + xc3 , etc. dans votre fichier.

si vous voulez lire et écrire des fichiers encodés en Python, utilisez au mieux le module codecs .

Collage texte entre le terminal et les applications est difficile, parce que vous ne savez pas quel programme interprétera votre texte en utilisant quel encodage. Vous pouvez essayer ce qui suit:

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

collez ensuite cette chaîne dans votre éditeur et assurez-vous qu'il le stocke en utilisant le Latin-1. Dans l'hypothèse où le bloc-notes ne gargouille pas la corde, le voyage aller-retour devrait fonctionner.

4
répondu Torsten Marek 2017-01-04 18:11:07

vous avez trébuché sur le problème général avec les encodages: Comment puis-je dire dans quel encodage un fichier est?

réponse: vous ne pouvez pas à moins que le format de fichier prévoit pour cela. Le XML, par exemple, commence par:

<?xml encoding="utf-8"?>

cet en-tête a été choisi avec soin afin qu'il puisse être lu quel que soit le codage. Dans votre cas, il n'existe pas d'indice, donc ni l'éditeur, ni Python a une idée de ce qui se passe. Par conséquent, vous devez utiliser le module codecs et utiliser codecs.open(path,mode,encoding) qui fournit le bit manquant en Python.

de votre éditeur, vous devez vérifier s'il dispose d'une certaine façon à définir l'encodage d'un fichier.

le but de L'UTF-8 est de pouvoir encoder des caractères de 21 bits (Unicode) comme un flux de données de 8 bits (parce que c'est la seule chose que tous les ordinateurs dans le monde peuvent gérer). Mais comme la plupart des logiciels libres datent d'avant L'ère Unicode, ils n'ont pas de des outils pour attacher les informations d'Encodage aux fichiers sur le disque dur.

la prochaine édition est la représentation en Python. Ceci est parfaitement expliqué dans le commentaire de heikogerlach . Vous devez comprendre que votre console ne peut afficher que L'ASCII. Pour afficher Unicode ou quoi que ce soit >= charcode 128, Il doit utiliser un moyen d'échapper. Dans votre éditeur, vous devez tapez pas échappé chaîne d'affichage, mais ce que la chaîne moyens (dans ce cas, vous devez entrez le tréma et enregistrer le fichier).

cela dit, Vous pouvez utiliser la fonction Python eval () pour transformer une chaîne échappée en une chaîne:

>>> x = eval("'Capit\xc3\xa1n\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

comme vous pouvez le voir, la chaîne" \xc3 " a été transformée en un seul caractère. C'est maintenant une chaîne de 8 bits, encodée UTF-8. Pour obtenir Unicode:

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind a demandé: je pense qu'il manque quelques pièces ici: le fichier f2 contient: hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

codecs.open('f2','rb', 'utf-8') , par exemple, les Lit tous dans un caractères séparé (prévu) y a-t-il un moyen d'écrire dans un fichier en ASCII qui fonctionnerait?

réponse: Cela dépend de ce que vous voulez dire. ASCII ne peut pas représenter les caractères > 127. Donc, vous avez besoin d'une certaine façon de dire "les prochains caractères signifient quelque chose de spécial" qui est ce que la séquence "\x" fait. Il est écrit: les deux caractères suivants sont le code d'un seul caractère. "\u" fait la même chose en utilisant quatre caractères pour encoder Unicode jusqu'à 0xFFFF (65535).

donc vous ne pouvez pas écrire directement Unicode à ASCII (parce que ASCII ne contient tout simplement pas les mêmes caractères). Vous pouvez l'écrire comme une chaîne de caractères s'échappe (comme dans f2); dans ce cas, le fichier peut être représenté comme ASCII. Ou vous pouvez l'écrire comme UTF-8, dans ce cas, vous avez besoin d'un 8-bit flux sûr.

votre solution en utilisant decode('string-escape') fonctionne, mais vous devez savoir combien de mémoire vous utilisez: trois fois le montant de l'utilisation codecs.open() .

rappelez-vous qu'un fichier est juste une séquence d'octets avec 8 bits. Ni les bits ni les octets n'ont de sens. C'est toi qui dis "65 veut dire A". Depuis \xc3\xa1 devrait devenir" à " mais l'ordinateur n'a aucun moyen de savoir, vous devez le dire en spécifiant le codage qui a été utilisé lors de l'écriture du fichier.

4
répondu Aaron Digulla 2017-05-23 11:47:25

le \X.. sequence est quelque chose qui est spécifique à Python. Ce n'est pas une séquence d'évasion universelle.

la façon dont vous entrez dans UTF-8-encoded non-ASCII dépend de votre système D'exploitation et/ou de votre éditeur. Voici comment vous le faites dans Windows . Pour OS X pour entrer a avec un accent aigu, vous pouvez juste appuyer sur option + E , puis a , et presque tout le texte les éditeurs D'OS X prennent en charge L'UTF-8.

3
répondu ʞɔıu 2017-01-04 18:09:13

vous pouvez aussi améliorer la fonction originale open() pour travailler avec des fichiers Unicode en la remplaçant en place, en utilisant la fonction partial . La beauté de cette solution est que vous n'avez pas besoin de changer les vieux code. Il est transparent.

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')
2
répondu hipertracker 2017-01-04 18:47:44

j'essayais de parser iCal en utilisant Python 2.7.9:

du calendrier d'importation icalendar

Mais je voulais en venir:

 Traceback (most recent call last):
 File "ical.py", line 92, in parse
    print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)

et il a été fixé avec juste:

print "{}".format(e[attr].encode("utf-8"))

(maintenant il peut imprimer comme á böss.)

0
répondu Alexx Roche 2017-01-04 18:45:31