Transformer des caractères spéciaux en ASCII
samedi 1 décembre 2012 à 15:23Dans beaucoup de cas, plutôt que de se taper la gestion de l’encodage, on préfère tout ramener au plus petit dénominateur commun: l’ASCII. Pas d’accent, pas de problème, comme disait mon grand-père juif. Ça devait être un autre contexte. Mais quand même.
Remplacer les accents par leurs équivalents ASCII les plus proches
Une astuce que j’ai lue pour la toute première fois dans les excellents snippets Python de Sauvage (encore et toujours…).
>>> import unicodedata >>> chaine_unicode = u"Vous êtes le Père Noël ? s'étonna le petit garçon." >>> unicodedata.normalize('NFKD', chaine_unicode).encode('ascii', 'ignore') "Vous etes le Pere Noel ? s'etonna le petit garcon."
Si un caractère n’est pas vraiment remplaçable, on peut choisir plusieurs stratégies. Ignorer le caractère:
>>> unicodedata.normalize('NFKD', u"Bei Jing en chinois s'écrit 北亰").encode('ascii', 'ignore') "Bei Jing en chinois s'ecrit "
Remplacer le caractère par un point d’interrogation:
>>> unicodedata.normalize('NFKD', u"Bei Jing en chinois s'écrit 北亰").encode('ascii', 'replace') "Bei Jing en chinois s'e?crit ??"
Notez l’effet sur le caractère accentué…
Ou se vautrer comme une grosse loutre bourrée à la bière:
>>> unicodedata.normalize('NFKD', u"Bei Jing en chinois s'écrit 北亰").encode('ascii') Traceback (most recent call last): File "<ipython-input-21-24181c80fc7f>", line 1, in <module> unicodedata.normalize('NFKD', u"Bei Jing en chinois s'écrit 北亰").encode('ascii') UnicodeEncodeError: 'ascii' codec can't encode character u'\u0301' in position 23: ordinal not in range(128)
Ce qui pour nous a un intérêt limité.
Certains alphabets, comme le hongrois, ont des caractères qui pourraient être extrapolés vers l’ASCII, mais la normalisation NFKD ne le permet pas. On peut alors faire appel à une bibliothèque externe.
Unidecode, la cafetière de l’encoding
Unidecode est une lib pip installable qui permet la translittération de caractères. Ce n’est pas toujours très rigoureux, mais c’est toujours très pratique.
Par exemple, vous voulez créer un site Web de mode à destination du marché Hongrois. Si, si. C’est un marché en pleine expansion, j’vous dis.
Et bien entendu vous avez une rubrique manteau, qui se dit en hongrois, comme nous le savons tous: “kožušček”.
Bon, si vous essayé de mettre ça en slug dans vos URL, ça va être un peu chiant à taper, et malheureusement l’astuce précédente ne va pas vous aider:
In [5]: unicodedata.normalize('NFKD', u"kožušček").encode('ascii', 'replace') Out[5]: 'koz?us?c?ek'
Partant de là, vous pouvez soit:
- choisir d’inviter tous vos utilisateurs à lire l’éloge des femmes mûres dans la langue de l’auteur et arrêter d’acheter des manteaux de merde qui sont de toute façon faits en Chine.
- soit utiliser
unidecode
.
In [7]: from unidecode import unidecode In [8]: unidecode(u"kožušček") Out[8]: 'kozuscek'
Encore plus fastoche, dans le XML et HTML
Dans ces formats à balise, on peut s’affranchir des problème d’encodage en transformant chaque caractère en une entité XML / HTML. On récupère alors du texte ASCII et le client se tapera le boulot d’afficher tous les caractères tordus auxquels l’humanité a pu penser:
In [11]: print u"Le Père Noël m'a offert un kožušček à 北亰".encode('ascii', 'xmlcharrefreplace') Le Père Noël m'a offert un kožušček à 北亰
Et dans tous les autres cas ?
Ben c’est comme d’hab: str.decode()
pour tout ce qui rentre pour récupérer une chaîne unicode, et unicode.encode()
pour tout ce qui sort pour rebalancer une chaîne de caractères au monde extérieur.
Ça mérite peut être un article d’ailleurs…
P.S: Lazarus vient de me sauver la mise en restaurant cet article suite à un clic malheureux. Vive lazarus.