Recherche de site Web

Comment travailler avec Unicode en Python


L'auteur a choisi le Fonds Libre et Open Source pour recevoir un don dans le cadre du programme Write for DOnations.

Introduction

Unicode est la norme d'encodage des caractères pour la majorité des ordinateurs du monde. Il garantit que le texte - y compris les lettres, les symboles, les emojis et même les caractères de contrôle - apparaît de la même manière sur différents appareils, plateformes et documents numériques, quelle que soit le système d'exploitation ou le logiciel utilisé. C'est une partie importante de l'internet et de l'industrie informatique, et sans lui, l'internet serait beaucoup plus chaotique et difficile à utiliser.

Unicode lui-même n'est pas un encodage mais ressemble plutôt à une base de données de presque tous les caractères possibles sur terre. Unicode contient un point de code, un identifiant pour chaque caractère dans sa base de données, qui peut avoir une valeur allant de 0 à 1,1 million, ce qui signifie qu'il est très peu probable qu'il manque ces points de code uniques de sitôt. Chaque point de code dans Unicode est représenté sous la forme U+n, où U+ signifie qu'il s'agit d'un point de code Unicode et n est l'ensemble de quatre à six chiffres hexadécimaux pour le caractère. C'est un système d'encodage beaucoup plus robuste que l'ASCII, qui ne représente que 128 caractères. Échanger du texte numérique dans le monde entier avec l'ASCII était difficile car il est basé sur l'anglais américain, sans support pour les caractères accentués. Unicode, en revanche, a presque 150 000 caractères et couvre les caractères de toutes les langues sur terre.

Avec cela vient une exigence pour les langages de programmation, tels que Python, de gérer correctement le texte et de permettre aux logiciels d'atteindre l'internationalisation. Python peut être utilisé pour un large éventail de choses, allant des courriels aux serveurs en passant par le web, et a une manière élégante de gérer l'Unicode, ce qu'il fait en adoptant la norme Unicode pour ses chaînes.

Cependant, travailler avec Unicode en Python peut être déroutant et mener à des erreurs. Ce tutoriel fournira les fondamentaux sur comment utiliser Unicode en Python pour vous aider à éviter ces problèmes. Vous utiliserez Python pour interpréter Unicode, normaliser des données avec les fonctions de normalisation de Python, et gérer les erreurs Unicode en Python.

Conditions préalables

Pour suivre ce tutoriel, vous aurez besoin de :

  • Python installé localement ou sur un serveur distant. Si vous n'avez pas encore configuré Python, vous pouvez le faire en suivant notre tutoriel Comment installer Python 3 et configurer un environnement de programmation. Choisissez la version qui convient à votre distribution Linux.
  • Connaissance de base de la programmation Python et des méthodes de chaîne de caractères de Python
  • Connaissance de la façon de travailler avec la console interactive Python

Étape 1 — Conversion des points de code Unicode en Python

L'encodage est le processus de représentation des données sous une forme lisible par l'ordinateur. Il existe de nombreuses façons d'encoder des données : ASCII, Latin-1, et plus encore, et chaque codage a ses propres forces et faiblesses, mais peut-être le plus courant est UTF-8. C'est un type d'encodage qui permet de représenter des caractères du monde entier dans un seul jeu de caractères. En tant que tel, UTF-8 est un outil essentiel pour quiconque travaillant avec des données internationalisées. En général, UTF-8 est un bon choix pour la plupart des usages. Il est relativement efficace et peut être utilisé avec une variété de logiciels. UTF-8 prend le code point Unicode et le convertit en octets hexadécimaux que l'ordinateur peut comprendre. En d'autres termes, Unicode est le mappage, et UTF-8 permet à un ordinateur de comprendre ce mappage.

En Python 3, l'encodage de chaîne par défaut est UTF-8, ce qui signifie que tout point de code Unicode dans la chaîne Python est automatiquement converti en caractère correspondant.

Dans cette étape, vous allez créer le symbole de copyright (©) en utilisant son point de code Unicode dans Python. Tout d'abord, ouvrez la console interactive Python dans votre terminal et tapez ce qui suit :

>>> s =  '\u00A9'
>>> s

Dans le code précédent, vous avez créé une chaîne s avec un point de code Unicode \u00A9. Comme mentionné précédemment, puisque la chaîne Python utilise par défaut l'encodage UTF-8, l'impression de la valeur de s la transforme automatiquement en symbole Unicode correspondant. Notez que le \u au début d'un point de code est nécessaire. Sans cela, Python ne pourra pas convertir le point de code. La sortie du code précédent renvoie le symbole Unicode correspondant :

'©'

Le langage de programmation Python fournit des fonctions intégrées pour encoder et décoder des chaînes. La fonction encode() convertit une chaîne en une chaîne d'octets.

Pour démontrer cela, ouvrez la console interactive Python et tapez le code suivant :

>>> '🅥'.encode('utf-8')

Cela produit la chaîne d'octets du caractère en sortie :

b'\xf0\x9f\x85\xa5'

Notez que chaque octet a un \x le précédant, ce qui indique qu'il s'agit d'un nombre hexadécimal.

Remarque : La saisie des caractères Unicode spéciaux est différente sous Windows et sur un Mac. Dans le code précédent, ainsi que dans tout le code de ce tutoriel qui utilise des symboles, vous pouvez insérer les symboles en utilisant l'utilitaire de carte des caractères sous Windows. Les Macs n'ont pas cette fonctionnalité, donc votre meilleure option est de copier le caractère à partir des exemples de code.

Ensuite, vous utiliserez la fonction decode() pour convertir la chaîne d'octets en une chaîne. La fonction decode() accepte le type d'encodage comme argument. Il convient également de mentionner que la fonction decode() ne peut déchiffrer que la chaîne d'octets, qui est spécifiée en utilisant la lettre b au début de la chaîne. Enlevant le b entraînerait une erreur AttributeError.

Dans votre console, tapez :

>>> b'\xf0\x9f\x85\xa5'.decode('utf-8')

Le code renverra un résultat comme ceci :

'🅥'

Vous avez maintenant une compréhension fondamentale de l'interprétation Unicode en Python. Ensuite, vous allez plonger dans le module unicodedata intégré de Python pour effectuer des techniques Unicode avancées sur vos chaînes.

Étape 2 — Normaliser Unicode en Python

Dans cette étape, vous normaliserez l'Unicode dans Python. La normalisation aide à déterminer si deux caractères écrits dans des polices différentes sont les mêmes, ce qui est utile lorsque deux caractères ayant des points de code différents produisent le même résultat. Par exemple, le caractère Unicode R et ℜ sont identiques à l'œil humain, car ce sont tous deux la lettre R, mais un ordinateur les considère comme des caractères différents.

L'exemple de code suivant illustre cela davantage. Ouvrez votre console Python et tapez ce qui suit :

>>> styled_R = 'ℜ'
>>> normal_R = 'R'
>>> styled_R == normal_R

Vous obtiendrez la sortie suivante :

False

Le code imprime False comme sortie car les chaînes Python ne considèrent pas les deux caractères comme identiques. Cette capacité à faire la différence est la raison pour laquelle la normalisation est importante lors du travail avec Unicode.

En Unicode, certains caractères sont créés en combinant deux ou plusieurs caractères en un seul. La normalisation est importante dans ce cas car elle maintient vos chaînes cohérentes entre elles. Pour mieux comprendre cela, ouvrez votre console Python et tapez le code suivant :

>>> s1 =  'hôtel'
>>> s2 = 'ho\u0302tel'
>>> len(s1), len(s2)

Dans le code précédent, vous avez créé une chaîne s1 contenant le caractère ô, et à la deuxième ligne, la chaîne s2 contient le point de code du caractère circonflexe ( ̂ ). Après exécution, le code renvoie la sortie suivante :

(5, 6)

La sortie précédente montre que les deux chaînes sont composées des mêmes caractères mais ont des longueurs différentes, ce qui signifie qu'elles échoueront à l'égalité. Tapez ce qui suit dans la même console pour le tester :

>>> s1 == s2

Le code renvoie la sortie suivante :

False

Bien que les variables de chaîne s1 et s2 produisent le même caractère Unicode, elles diffèrent en longueur et ne sont donc pas égales.

Vous pouvez résoudre ce problème avec la fonction normalize(), ce que vous ferez à l'étape suivante.

Étape 3 — Normalisation Unicode avec NFD, NFC, NFKD et NFKC

Dans cette étape, vous normaliserez des chaînes Unicode avec la fonction normalize() de la bibliothèque unicodedata de Python dans le module unicodedata, qui fournit des capacités de recherche et de normalisation des caractères. La fonction normalize() peut prendre une forme de normalisation comme premier argument et la chaîne à normaliser comme deuxième argument. Unicode a quatre types de formes de normalisation que vous pouvez utiliser pour cela : NFD, NFC, NFKD et NFKC.

La forme de normalisation NFD décompose un caractère en plusieurs caractères diacritiques. Cela rend votre texte insensible aux accents, ce qui peut être utile pour la recherche et le tri. Vous pouvez le faire en encodant la chaîne en octets.

Ouvrez votre console et tapez ce qui suit :

>>> from unicodedata import normalize
>>> s1 =  'hôtel'
>>> s2 = 'ho\u0302tel'
>>> s1_nfd = normalize('NFD', s1)
>>> len(s1), len(s1_nfd)

Le code produit la sortie suivante :

(5, 6)

Comme l'exemple le montre, normaliser la chaîne s1 augmente sa longueur d'un caractère. Cela est dû au fait que le symbole ô est divisé en deux caractères, o et ˆ, ce que vous pouvez confirmer en utilisant le code suivant:

>>> s1.encode(), s1_nfd.encode()

La sortie résultante révèle qu'après avoir encodé la chaîne normalisée, le caractère o s'est séparé du caractère ˆ dans la chaîne s1_nfd :

(b'h\xc3\xb4tel', b'ho\xcc\x82tel')

La forme de normalisation NFC décompose d'abord un caractère, puis le recombine avec tout caractère combinant disponible. Comme NFC compose une chaîne pour produire la sortie la plus courte possible, le W3C recommande d'utiliser NFC sur le web. La saisie au clavier renvoie par défaut des chaînes composées, donc il est judicieux d'utiliser NFC dans ce cas.

Par exemple, tapez ce qui suit dans votre console interactive :

>>> from unicodedata import normalize
>>> s2_nfc = normalize('NFC', s2)
>>> len(s2), len(s2_nfc)

Le code produit la sortie suivante :

(6, 5)

Dans l'exemple, normaliser la chaîne s2 diminue sa longueur d'un. Vous pouvez le confirmer en exécutant le code suivant dans votre console interactive :

>>> s2.encode(), s2_nfc.encode()

La sortie du code est :

(b'ho\xcc\x82tel', b'h\xc3\xb4tel')

La sortie montre que les caractères o et ˆ ont fusionné en un seul caractère ô.

Les formes de normalisation NFKD et NFKC sont utilisées pour une normalisation “stricte” et peuvent être utilisées pour une variété de problèmes liés à la recherche et au correspondance de motifs dans les chaînes Unicode. Le “K” dans NFKD et NFKC signifie compatibilité.

Les formes de normalisation NFD et NFC décomposent les caractères, mais NFKD et NFKC effectuent une décomposition de compatibilité pour les caractères qui ne sont pas similaires mais qui sont équivalents, supprimant toute distinction de formatage. Par exemple, la chaîne ②① n'est pas similaire à 21, mais elles représentent toutes deux la même valeur. Les formes de normalisation NFKC et NFKD suppriment ce formatage (dans ce cas, le cercle autour des chiffres) des caractères afin de fournir la forme la plus épurée possible.

L'exemple suivant démontre la différence entre les formes de normalisation NFD et NFKD. Ouvrez votre console interactive Python et tapez ce qui suit :

>>> s1 = '2⁵ô'
>>> from unicodedata import normalize
>>> normalize('NFD', s1), normalize('NFKD', s1)

Vous obtiendrez la sortie suivante :

('2⁵ô', '25ô')

La sortie révèle que la forme NFD n'a pas pu décomposer le caractère exposant dans la chaîne s1, mais NFKD a supprimé le formatage de l'exposant et a remplacé le caractère de compatibilité (dans ce cas, l'exposant 5) par son équivalent (5 en tant que chiffre). Rappelez-vous que les formes de normalisation NFD et NFKD décomposent toujours les caractères, donc le caractère ô devrait augmenter sa longueur d'une unité, comme vous l'avez vu dans l'exemple NFD précédent. Vous pouvez le confirmer en exécutant le code suivant :

>>> len(normalize('NFD', s1)), len(normalize('NFKD', s1))

Le code renverra ce qui suit :

(4, 4)

La forme de normalisation NFKC fonctionne de manière similaire, mais compose des caractères plutôt que de les décomposer. Dans la même console Python, tapez ce qui suit :

>>> normalize('NFC', s1), normalize('NFKC', s1)

Le code renvoie ce qui suit :

('2⁵ô', '25ô')

Puisque NFKC suit l'approche de composition, vous devez vous attendre à ce que la chaîne pour le caractère ô soit réduite d'un, plutôt que d'être augmentée d'un dans le cas de décomposition. Vous pouvez le confirmer en exécutant la ligne de code suivante :

>>> len(normalize('NFC', s1)), len(normalize('NFKC', s1))

Cela renverra la sortie suivante :

(3, 3)

En effectuant les étapes précédentes, vous aurez une connaissance pratique des types de formes de normalisation et des différences entre elles. Dans l'étape suivante, vous résoudrez les erreurs Unicode en Python.

Étape 4 — Résoudre les erreurs Unicode dans Python

Deux types d'erreurs Unicode peuvent survenir lors de la manipulation de l'Unicode en Python, l'UnicodeEncodeError et l'UnicodeDecodeError. Bien que ces erreurs Unicode puissent être déroutantes, elles peuvent être gérées, et vous allez corriger ces deux erreurs à cette étape.

Résoudre une UnicodeEncodeError

L'encodage en Unicode est le processus de conversion de la chaîne Unicode en octets à l'aide d'un encodage particulier. Une erreur UnicodeEncodeError se produit lorsqu'on essaie d'encoder une chaîne contenant des caractères qui ne peuvent pas être représentés dans l'encodage spécifié.

Pour créer cette erreur, vous allez encoder une chaîne qui contient des caractères qui ne font pas partie de l'ensemble de caractères ASCII.

Ouvrez votre console et tapez ce qui suit :

>>> ascii_supported = '\u0041'
>>> ascii_supported.encode('ascii')

Voici votre résultat :

b'A'

Puis tapez ce qui suit :

>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('utf-8')

Vous obtiendrez la sortie suivante :

b'\xef\xac\x86'

Enfin, tapez ce qui suit :

>>> ascii_unsupported.encode('ascii')

Lorsque vous exécutez ce code, cependant, vous obtiendrez l'erreur suivante :

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\ufb06' in position 0: ordinal not in range(128)

L'ASCII a un nombre limité de caractères, et Python renvoie des erreurs lorsqu'il trouve un caractère qui n'est pas disponible dans le jeu de caractères ASCII. Puisque le jeu de caractères ASCII ne reconnaît pas le point de code \ufb06, Python retourne un message d'erreur indiquant que l'ASCII a une plage de seulement 128 et que l'équivalent décimal correspondant de ce point de code n'est pas dans cette plage.

Vous pouvez gérer l'erreur UnicodeEncodeError en utilisant un argument errors dans la fonction encode(). L'argument errors peut avoir l'une des trois valeurs : ignore, replace, et xmlcharrefreplace.

Ouvrez votre console et tapez ce qui suit :

>>> ascii_unsupported = '\ufb06'
>>> ascii_unsupported.encode('ascii', errors='ignore')

Vous obtiendrez le résultat suivant

b''

Tapez le texte suivant :

>>> >>> ascii_unsupported.encode('ascii', errors='replace')

La sortie sera :

b'?'

Enfin, tapez ce qui suit :

>>> ascii_unsupported.encode('ascii', errors='xmlcharrefreplace')

La sortie est :

b'&#64262;'

Dans chaque cas, Python ne génère pas d'erreur.

Comme le démontre l'exemple précédent, ignore ignore le caractère qui ne peut pas être encodé, replace remplace le caractère par un ?, et xmlcharrefreplace remplace les caractères non encodables par une entité XML.

Résoudre une UnicodeDecodeError

Une UnicodeDecodeError se produit lorsqu'on essaie de décoder une chaîne qui contient des caractères qui ne peuvent pas être représentés dans l'encodage spécifié.

Pour créer cette erreur, vous allez essayer de décoder une chaîne d'octets dans un encodage qui ne peut pas être décodé.

Ouvrez votre console et tapez ce qui suit :

>>> iso_supported = '§'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8')

Vous obtiendrez l'erreur suivante :

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa7 in position 0: invalid start byte

Si vous rencontrez cette erreur, vous pouvez utiliser l'argument errors dans la fonction decode(), ce qui peut vous aider à décoder la chaîne. L'argument errors accepte deux valeurs, ignore et replace.

Pour démontrer cela, ouvrez votre console Python et tapez le code suivant :

>>> iso_supported = '§A'
>>> b = iso_supported.encode('iso8859_1')
>>> b.decode('utf-8', errors='replace')

Votre sortie sera :

'�A'

Puis tapez ce qui suit :

>>> b.decode('utf-8', errors='ignore')

Votre sortie sera :

'A'

Dans l'exemple précédent, l'utilisation de la valeur replace dans la fonction decode() a ajouté un caractère �, et l'utilisation de ignore n'a rien renvoyé là où le décodeur (dans ce cas utf-8) n'a pas pu décoder les octets.

Lors de la décodage d'une chaîne, veuillez noter que vous ne pouvez pas présumer de l'encodage. Pour décoder une chaîne, vous devez savoir comment elle a été encodée.

Conclusion

Cet article a couvert les fondamentaux de l'utilisation de l'Unicode en Python. Vous avez encodé et décodé des chaînes, normalisé des données en utilisant NFD, NFC, NFKD et NFKC, et résolu des erreurs Unicode. Vous avez également utilisé des formes de normalisation dans des scénarios impliquant le tri et la recherche. Ces techniques vous aideront à gérer les problèmes d'Unicode en utilisant Python. Comme prochaine étape, vous pouvez lire la documentation du module unicodedata pour en savoir plus sur d'autres fonctionnalités offertes par ce module. Pour continuer à explorer comment programmer avec Python, lisez notre série de tutoriels Comment coder en Python 3.