Blog ENI : Toute la veille numérique !
💥 Un livre PAPIER acheté
= La version EN LIGNE offerte pendant 1 an !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Chaînes de caractères et algorithmes appliqués

Présentation

1. Définition

Une chaîne de caractères est une collection ordonnée et modifiable de caractères. Il n’y a forcément pas de notions de doublons, puisque cette notion n’a pas de sens pour une chaîne de caractères. L’ordre est important puisqu’il s’agit de l’ordre de lecture, sans lui l’objet n’a pas de sens.

Pour Python 3, les méthodes disponibles sont :

>>> dir(str) 
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', 
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', 
'__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', 
'__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 
'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 
'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 
'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 
'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 
'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 
'rstrip'...

Formatage de chaînes de caractères

1. Opérateur modulo

Les chaînes de caractères ont une implémentation spécifique de l’opérateur modulo. Celui-ci sert à faire ce que le langage C réalise avec printf, mais les fonctionnalités proposées sont encore plus impressionnantes.

Derrière le modulo, il ne peut y avoir qu’un seul objet. Si l’on doit en passer plusieurs, il faut donc utiliser un n-uplet, et à cause des priorités, il faut absolument utiliser les parenthèses.

>>> 'ceci est %s' % 'une chaine' 
'ceci est une chaine' 
>>> '%s est %s' % ('ceci', 'une chaine') 
'ceci est une chaine' 
>>> '%s est %s' % 'ceci', 'une chaine' 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: not enough arguments for format string 

Nous allons voir la diversité des formatages possibles.

Restons simples avec l’utilisation de %s :

>>> '%s' % 'chaine' 
'chaine' 
>>> '%s' % 1 
'1' 
>>> '%s' % 1.0 
'1.0' 
>>> '%s' % 1.000 
'1.0' 
>>> '%s' % 1. 
'1.0' 
>>> '%s' % str 
"<class 'str'>" 

La conversion est réalisée à l’aide de la méthode __str__ de chaque objet et il est possible de se référer à la méthode __repr__ en utilisant %rau lieu de %s.

Par ordre de fréquence d’utilisation, viennent les entiers :

>>> '%d' % 1 
'1' 
>>> '%d' % 1.0 
'1' ...

Opérations d’ensemble

1. Séquençage de chaînes

Une chaîne de caractères est, comme on l’a vu, itérable et peut être utilisée comme une liste, sur certains aspects :

>>> for c in 'char': 
...     print(c)  
...  
c 
h 
a 
r 

En cas de besoin, il est facile de transformer une chaîne de caractères en liste de caractères :

>>> s = 'chaîne de caractères' 
>>> l = list(s) 
>>> l 
['c', 'h', 'a', 'î', 'n', 'e', ' ', 'd', 'e', ' ', 'c', 'a', 'r',  
'a', 'c', 't', 'è', 'r', 'e', 's'] 

Une telle opération est rarement utile, car le type de données chaîne de caractères dispose de tout ce dont un développeur peut avoir besoin, mais la conversion reste possible. Une autre conversion possible est celle vers les ensembles :

>>> e = set(s) 
>>> s 
'chaîne de caractères' 
>>> e 
{'a', ' ', 'c', 'e', 'd', 'i', 'h', 'n', 's', 'r', 't'} 

Grâce à cet outil, on peut connaître la liste des lettres utilisées dans une chaîne et, en utilisant les mathématiques ensemblistes, comparer cette liste avec une autre chaîne et savoir ce qu’elles partagent :

>>> s2 = "autre chaine" 
>>> e2 = set(s2) 
>>> e2 
{'a', ' ', 'c', 'e', 'i', 'h', 'n', 'r'...

Problématiques relatives à l’encodage

1. Encodage par défaut

En Python 2.x, l’encodage par défaut est l’ASCII :

>>> import sys 
>>> sys.getdefaultencoding() 
'ascii' 

En Python 3.x, c’est l’UTF-8 :

>>> import sys 
>>> sys.getdefaultencoding() 
'utf-8' 

2. Encodage du système

En Python 2.x, l’encodage du système est bien reconnu :

>>> import sys 
>>> sys.getfilesystemencoding() 
'UTF-8' 

En Python 3.x, il ne se représente pas exactement de la même manière, mais c’est le même :

>>> import sys 
>>> sys.getfilesystemencoding() 
'utf-8' 

Dans les deux cas, la reconnaissance s’effectue correctement.

3. L’unicode, référence absolue

L’unicode est un encodage qui a été créé pour remplacer les encodages régionaux, nationaux ou semi-continentaux pour englober en son sein tous les caractères utilisés par les autres encodages.

Cela signifie que tous les autres encodages en sont des sous-ensembles et que par conséquent, il est très aisé de passer de l’unicode vers un autre encodage, soit d’un grand ensemble vers un plus petit.

Plusieurs encodages sont semblables ou partagent au sein de leur table des caractères communs. Les normes sont nombreuses et parfois génératrices de confusion, puisque ayant beaucoup évolué dans l’histoire de l’informatique, liée à la fois à l’évolution de l’Histoire (apparition du symbole ), des matériels et des problématiques de bas niveau.

Le résultat d’un encodage d’une chaîne unicode donne un objet de type bytes :

>>> test...

Manipulations de bas niveau avancées

1. Opérations de comptage

Voici une chaîne de caractères représentant un texte, tel qu’il peut être stocké de manière persistante et retrouvé par des procédés usuels.

>>> s='''Ceci est une courte phrase. Ceci en est une pas beaucoup  
plus longue. 
... Ceci est un autre paragraphe. 
... Ceci est le dernier paragraphe. 
... ''' 

Compter le nombre de signes est trivial :

>>> len(s) 
132 

Compter le nombre de phrases est plus délicat :

>>> len(s.split('.')) 
4 

Il faut en réalité prendre en compte tous les caractères de ponctuation de fins de phrases. La solution à venir est mauvaise, car elle crée des tableaux avec autant de dimensions que de caractères et oblige à des itérations lourdes à la fois dans la syntaxe et dans le traitement :

>>> temp=[a.split('!') for a in s.split('.')] 
>>> phrases=[] 
>>> for tmp in temp: 
...     phrases.extend(tmp) 
...  
>>> len(phrases) 
5 

Un algorithme similaire gérant simultanément le point, le point d’interrogation, le point d’exclamation, les points de suspension, les deux-points et le point-virgule de la même manière serait long à écrire et à exécuter. La solution la plus simple est donc de procéder à un remplacement avant de découper :

>>> phrases=s 
>>> for c in '?!...:;': 
...      phrases=phrases.replace(c, '.') 
...  
>>> len(phrases.split('.')) 
5 

Le comptage de mots est plus trivial (encore que l’on peut considérer que d’autres séparateurs...

Représentation mémoire

1. Présentation du type bytes

Maintenant qu’une chaîne de caractères n’est plus représentée que par le type unicode, le type bytes ne se voit plus que comme une représentation de bits, d’octets ou d’hexadécimaux, et est utilisée spécifiquement pour des problématiques de bas niveau.

Ainsi, une conversion d’une chaîne de caractères dans un jeu de caractères autre que l’unicode est considérée avant tout comme une série de bits. De même, un nombre entier est vu comme une série de bits.

Voici un moyen de visualiser comment les 256 premiers entiers sont représentés en bytes en parallèle avec la représentation octale et hexadécimale :

>>> def int_and_bytes(): 
...     print('+-----+---------+-------+------+') 
...     print('| int |  bytes  | octal | hexa |') 
...     print('+-----+---------+-------+------+') 
...     for i in range(256): 
...         print('| %3d | %-7s | %#-5o | %#-4x |' % (i,  
i.to_bytes(1, 'big'), i, i)) 
...     print('+-----+---------+-------+------+') 
... 

Le résultat de ce script se trouve en annexe.

Il est possible de transformer n’importe quel entier en bytes si la longueur de la représentation est suffisante :

>>> (10000).to_bytes(4, 'big') 
b"\x00\x00'\x10" 
>>> (10000).to_bytes(2, 'big') 
b"'\x10" 
>>> (10000).to_bytes(1, 'big') 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
OverflowError: int too big to convert 

Outre la longueur...