Je extrait des données d'un document Google, les traite et les écris dans un fichier (que je vais éventuellement coller dans une page Wordpress).
Il a des symboles non-ASCII. Comment puis-je convertir ces symboles en toute sécurité en symboles pouvant être utilisés dans une source HTML?
Actuellement, je convertis tout le contenu en Unicode, en le réunissant dans une chaîne Python, puis en faisant:
import codecs
f = codecs.open('out.txt', mode="w", encoding="iso-8859-1")
f.write(all_html.encode("iso-8859-1", "replace"))
Il y a une erreur d'encodage sur la dernière ligne:
UnicodeDecodeError: le codec 'ascii' ne peut pas décoder l'octet 0xa0 en position 12286: l'ordinal n'est pas dans la plage (128)
Solution partielle:
Ce Python s'exécute sans erreur:
row = [unicode(x.strip()) if x is not None else u'' for x in row]
all_html = row[0] + "<br/>" + row[1]
f = open('out.txt', 'w')
f.write(all_html.encode("utf-8"))
Mais ensuite, si j'ouvre le fichier texte, je vois beaucoup de symboles comme:
Qur’an
Peut-être que j'ai besoin d'écrire sur autre chose qu'un fichier texte?
Traitez le plus possible exclusivement avec des objets unicode en décodant des éléments en objets unicode lors de leur première acquisition et en les codant si nécessaire à la sortie.
Si votre chaîne est en réalité un objet unicode, vous devrez le convertir en objet chaîne encodé au format Unicode avant de l'écrire dans un fichier:
foo = u'Δ, Й, ק, م, ๗, あ, 叶, 葉, and 말.'
f = open('test', 'w')
f.write(foo.encode('utf8'))
f.close()
Lorsque vous relisez ce fichier, vous obtenez une chaîne codée au format Unicode que vous pouvez décoder en un objet Unicode:
f = file('test', 'r')
print f.read().decode('utf8')
Dans Python 2.6+, vous pourriez tiliser io.open()
c'est la valeur par défaut ( Builtin open()
) sur Python 3:
import io
with io.open(filename, 'w', encoding=character_encoding) as file:
file.write(unicode_text)
Cela peut être plus pratique si vous devez écrire le texte progressivement (vous n'avez pas besoin d'appeler plusieurs fois unicode_text.encode(character_encoding)
). Contrairement au module codecs
, le module io
prend en charge les nouvelles lignes universelles.
La gestion des chaînes Unicode est déjà normalisée dans Python 3.
Vous devez seulement ouvrir le fichier dans utf-8
(La conversion de 32 bits entre Unicode et UTF-8 est automatiquement effectuée de la mémoire au fichier.)
out1 = "(嘉南大圳 ㄐㄧㄚ ㄋㄢˊ ㄉㄚˋ ㄗㄨㄣˋ )"
fobj = open("t1.txt", "w", encoding="utf-8")
fobj.write(out1)
fobj.close()
Le fichier ouvert par codecs.open
est un fichier qui prend des données unicode
, les code dans iso-8859-1
et les écrit dans le fichier. Cependant, ce que vous essayez d'écrire n'est pas unicode
; vous prenez unicode
et vous l'encodez dans iso-8859-1
vous-même. C’est ce que fait la méthode unicode.encode
et le résultat de l’encodage d’une chaîne unicode est une chaîne bytest (un type str
.)
Vous devez soit utiliser la fonction normale open()
et coder vous-même le code unicode, soit (généralement une meilleure idée) utiliser codecs.open()
et not coder les données vous-même.
Assurez-vous que votre lecteur/éditeur/terminal (quelle que soit votre interaction avec votre fichier encodé en utf-8) puisse lire le fichier. C'est souvent un problème sur Windows , par exemple, le Bloc-notes.
Écrire du texte Unicode dans un fichier texte?
Dans Python 2, utilisez open
à partir du module io
(il en va de même pour la fonction intégrée open
dans Python 3):
import io
La meilleure pratique, en général, utilise UTF-8
pour écrire dans des fichiers (nous n'avons même pas à nous soucier de l'ordre des octets avec utf-8).
encoding = 'utf-8'
utf-8 est l'encodage le plus moderne et le plus universellement utilisable - il fonctionne dans tous les navigateurs Web, la plupart des éditeurs de texte (consultez vos paramètres si vous rencontrez des problèmes) et la plupart des terminaux/shells.
Sous Windows, vous pouvez essayer utf-16le
si vous êtes limité à l'affichage de la sortie dans le Bloc-notes (ou dans un autre visualiseur limité).
encoding = 'utf-16le' # sorry, Windows users... :(
Et ouvrez-le simplement avec le gestionnaire de contexte et écrivez vos caractères unicode:
with io.open(filename, 'w', encoding=encoding) as f:
f.write(unicode_object)
Voici un exemple qui tente de mapper tous les caractères possibles d’une largeur maximale de trois bits (4 est le maximum, mais cela irait un peu loin) de la représentation numérique (en entiers) à une sortie imprimable codée, avec son nom, si possible (placez-le dans un fichier nommé uni.py
):
from __future__ import print_function
import io
from unicodedata import name, category
from curses.ascii import controlnames
from collections import Counter
try: # use these if Python 2
unicode_chr, range = unichr, xrange
except NameError: # Python 3
unicode_chr = chr
exclude_categories = set(('Co', 'Cn'))
counts = Counter()
control_names = dict(enumerate(controlnames))
with io.open('unidata', 'w', encoding='utf-8') as f:
for x in range((2**8)**3):
try:
char = unicode_chr(x)
except ValueError:
continue # can't map to unicode, try next x
cat = category(char)
counts.update((cat,))
if cat in exclude_categories:
continue # get rid of noise & greatly shorten result file
try:
uname = name(char)
except ValueError: # probably control character, don't use actual
uname = control_names.get(x, '')
f.write(u'{0:>6x} {1} {2}\n'.format(x, cat, uname))
else:
f.write(u'{0:>6x} {1} {2} {3}\n'.format(x, cat, char, uname))
# may as well describe the types we logged.
for cat, count in counts.items():
print('{0} chars of category, {1}'.format(count, cat))
Cela devrait durer environ une minute. Vous pouvez visualiser le fichier de données. Si votre visionneuse de fichiers peut afficher unicode, vous le verrez. Des informations sur les catégories peuvent être trouvées ici . Sur la base des chiffres, nous pouvons probablement améliorer nos résultats en excluant les catégories Cn et Co, qui ne sont associées à aucun symbole.
$ python uni.py
Il affichera le mappage hexadécimal, catégorie , le symbole (à moins que vous ne puissiez pas obtenir le nom, donc probablement un caractère de contrôle), et le nom du symbole. par exemple.
Je recommande less
sous Unix ou Cygwin (n'imprimez pas/ne supprimez pas l'intégralité du fichier dans votre sortie):
$ less unidata
par exemple. affichera des lignes similaires aux lignes suivantes que j’ai échantillonnées à l’aide de Python 2 (unicode 5.2):
0 Cc NUL
20 Zs SPACE
21 Po ! EXCLAMATION MARK
b6 So ¶ PILCROW SIGN
d0 Lu Ð LATIN CAPITAL LETTER ETH
e59 Nd ๙ THAI DIGIT NINE
2887 So ⢇ BRAILLE PATTERN DOTS-1238
bc13 Lo 밓 HANGUL SYLLABLE MIH
ffeb Sm → HALFWIDTH RIGHTWARDS ARROW
Mon Python 3.5 d'Anaconda a unicode 8.0, je suppose que la plupart des 3 le seraient.
Comment imprimer des caractères Unicode dans un fichier:
Enregistrez ceci dans le fichier: foo.py:
#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
import codecs
import sys
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)
print(u'e with obfuscation: é')
Exécutez-le et dirigez la sortie vers le fichier:
python foo.py > tmp.txt
Ouvrez tmp.txt et regardez à l'intérieur, vous voyez ceci:
el@apollo:~$ cat tmp.txt
e with obfuscation: é
Ainsi, vous avez enregistré unicode avec un repère d’obscurcissement dans un fichier.
Cette erreur survient lorsque vous essayez de coder une chaîne non-unicode: elle tente de la décoder, en supposant que ce soit en ASCII brut. Il y a deux possibilités:
f.write(all_html)
à la place..encode(...)
, il essaie d'abord de le décoder.En cas d'écriture sur python3
>>> a = u'bats\u00E0'
>>> print a
batsà
>>> f = open("/tmp/test", "w")
>>> f.write(a)
>>> f.close()
>>> data = open("/tmp/test").read()
>>> data
'batsà'
En cas d'écriture sur python2:
>>> a = u'bats\u00E0'
>>> f = open("/tmp/test", "w")
>>> f.write(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)
Pour éviter cette erreur, vous devez l'encoder en octets en utilisant les codecs "utf-8" comme ceci:
>>> f.write(a.encode("utf-8"))
>>> f.close()
et décodez les données en lisant à l'aide des codecs "utf-8":
>>> data = open("/tmp/test").read()
>>> data.decode("utf-8")
u'bats\xe0'
Et aussi si vous essayez d'exécuter une impression sur cette chaîne, elle sera automatiquement décodée à l'aide des codecs "utf-8" comme celui-ci.
>>> print a
batsà