Transformer des caractères spéciaux en ASCII


Dans 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&#232;re No&#235;l m'a offert un ko&#382;u&#353;&#269;ek &#224; &#21271;&#20144;

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.

5 thoughts on “Transformer des caractères spéciaux en ASCII

  • Romain

    Je pense qu’à la place de “Unicode est une lib pip installable qui permet… ” il faut lire “Unidecode est une lib pip installable qui permet…”. En tout cas, ça a pas l’air mal comme lib. Merci pour l’info.

    (moi aussi je me demande pourquoi tout le monde n’est pas en ascii :()

  • Sam Post author

    Piqué sur le hollandais volant, la version PHP:

    $texte = preg_replace('#&(.)(acute|grave|circ|uml|cedil|tilde|ring|slash|caron);#', '$1', $texte);
  • Drife

    A noter qu’en python3:

    chaine_unicode = u”Vous êtes le Père Noël ? s’étonna le petit garçon.”

    unicodedata.normalize(‘NFKD’, chaine_unicode).encode(‘ascii’, ‘ignore’)

    b”Vous etes le Pere Noel ? s’etonna le petit garcon.”

    Car python3 indique explicitement si la chaine est encode en utf8 ou un ASCII. Dans ce dernier cas, la chaine est préfixée par “b” (pour byte).

    Pour retrouver un encodage utf8 classique, il suffit de faire un petit decode:

    unicodedata.normalize(‘NFKD’, chaine_unicode).encode(‘ascii’, ‘ignore’).decode()

    “Vous etes le Pere Noel ? s’etonna le petit garcon.”

    Voilà je me permet ce petit post maintenant que Python3 est la norme :-D.

  • Sam Post author

    Ouai mais y a 880 articles alors on est pas près de tous les corriger :)

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.