Les chaines de caractères

Une vidéo de présentation des chaînes de caractères…

Généralités

En Python tout est objet, et donc les chaînes de caractères sont des objets qui peuvent se définir à partir des simples ' ou des double " quotes:

>>> a = "hello"
>>> b = 'hello'
>>> c = "world"

Ici on a créé 3 objets « chaîne de caractères » :a, b et c.

Note

En programmation objet, la création d’un objet fait appel à un constructeur auquel on passe le contenu de l’objet à créer. Ainsi pour créer la chaîne de caractère « hello », on peut faire appel au constructeur de la classe str:

>>> a = str('hello')
>>> a
'hello'

Cependant les caractères " ou ' produisent le même résultat avec une syntaxe plus légère. On appelle ça du sucre syntaxique.

Chaque objet créé possède plusieurs caractéristiques parmi lesquelles :

  • son contenu ;

  • son identité, obtenue avec la fonction id().

On peut tester l’égalité du contenu de deux objets avec l’opérateur ==:

>>> a == b
True
>>> a == c
False

Ici, en plus d’avoir le même contenu, les variables a et b ont la même identité, c’est à dire sont deux références distinctes vers le même objet:

>>> id(a)
37940000
>>> id(b)
37940000

On peut vérifier que a et b sont deux références distinctes vers le même objet avec l’opérateur is:

>>> a is b
True

Note

Dans ce cas, lors de la création de b Python a trouvé un objet identique en mémoire et n’a pas éprouvé le besoin d’en créer un second. Il a simplement créé une nouvelle référence vers l’objet déjà référencé par a.

Une curiosité, lorsque la chaîne de caractère est plus longue, deux objets différents sont créés:

>>> a = "hello"*1000 # "hellohellohello...hello" 1000 fois
>>> b = "hello"*1000
>>> a is b
False

En généralisant l’exemple ci dessus, on peut avoir deux références o1 et o2 vers deux objets distincts contenant une même valeur. Dans ce cas:

>>> o1 == o2
True
>>> o1 is o2
False

On peut utiliser les délimiteurs dans la chaîne de caractère elle même, mais il faut les échapper avec \:

>>> s = "J'entends et j'oublie, je vois et je me souviens, je fais et je comprends"
>>> s
"J'entends et j'oublie, je vois et je me souviens, je fais et je comprends"
>>> s = 'J\'entends et j\'oublie, je vois et je me souviens, je fais et je comprends'
>>> s
"J'entends et j'oublie, je vois et je me souviens, je fais et je comprends"

On peut insérer des caractères spéciaux utilisés pour l’affichage: la tabulation \t et le retour à la ligne \n. Ces caractères apparaissent tels quels dans la chaîne de caractères mais sont interprétés correctement à l’affichage:

>>> s = "Veni,\t vidi,\t vici\n"
>>> s
'Veni,\t vidi,\t vici\n'
>>> print(s)
Veni,    vidi,  vici

>>>

Note

Il faut différencier la chaîne de caractère elle même, de sa représentation. Les caractères imprimables (lettres, chiffres, la plupart des caractères spéciaux) sont rendus tels quels, alors que les caractères non imprimables sont transformés par la fonction print() avant affichage. Par exemple \n est transformé en saut de ligne.

Les chaînes multilignes, que l’on a déjà aperçues dans les docstring, utilisent un triple séparateur:

>>> s = """
... Si tu peux voir détruit l'ouvrage de ta vie
... Et sans dire un seul mot te mettre à rebâtir
... Ou perdre en un seul coup le gain de cent parties
... Sans un geste et sans un soupir
... """
>>> s
"Si tu peux voir détruit l'ouvrage de ta vie\nEt sans dire un seul mot te mettre
 à rebâtir\nOu perdre en un seul coup le gain de cent parties\nSans un geste et
sans un soupir\n"
>>> print(s)
Si tu peux voir détruit l'ouvrage de ta vie
Et sans dire un seul mot te mettre à rebâtir
Ou perdre en un seul coup le gain de cent parties
Sans un geste et sans un soupir

Python est un langage permettant la redéfinition des opérateurs, ce qui permet de les adapter au contexte de l’objet. Pour les chaînes de caractères, l’opérateur + a été redéfini comme opérateur de concaténation:

>>> 'Hello ' + 'world ' + '!'
'Hello world !'

Et comme on l’a aperçu précédemment, l’opérateur * a également été redéfini:

>>> 'Hi '*3
'Hi Hi Hi '

Les chaînes de caractères sont des séquences

Les séquences jouent un grand rôle dans Python. Une séquence est une collection ordonnées d’objets. Pour le cas particulier des chaînes de caractères, il s’agit évidemment d’une collection ordonnée de caractères.

On peut connaitre la longueur d’une séquence avec la fonction len():

>>> s = "Hello World!"
>>> len(s) # longueur de la chaine
13

Le premier caractère de la chaîne est à l’index 0, le dernier à l’index len(s)-1:

>>> s[0] # premier élément de la chaine
'H'
>>> s[len(s)-1] # dernier élément de la chaine
'!'
>>> s[8]
'r'

Python utilise les index négatifs pour utiliser la fin de chaine comme référence, plutôt que le début:

>>> s[-1] # dernier élément de la liste
'!'
>>> s[-3]
'l'

Une façon simple de visualiser ce fonctionnement est de considérer que les index définissent les intervalles plutôt que les éléments eux mêmes.

../_images/05-slice.png

Lorsque l’index est positif, on prend l’élément à droite:

>>> s[8]
'r'

Lorsque l’index est négatif, on prend l’élément à gauche:

>>> s[-6]
'o'

Si on essaye d’accéder à un élément qui n’existe pas, une erreur se produit:

>>> s[14]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

Comme exposé précédemment, la dernière ligne décrit la nature de l’erreur en langage naturel et donne une piste pour la résolution du problème.

Slicing

On peut découper la chaîne de caractère avec la syntaxe s[d:f:p] où :

  • d représente l’index de début ;

  • f l’index de fin ;

  • et p le pas.

Dans cette syntaxe, le premier index d est inclus, le dernier f ne l’est pas. s[d:f] produit donc la chaîne de caractère composée des éléments s[d] à s[f-1]. On prendra donc tous les éléments à droite de d et à gauche de f:

>>> s[0:3]
'Hel'

Il y a deux avantages majeurs à cette syntaxe, illustrés par les deux propriétés suivantes :

  • s[:i] + s[i:] = s

  • f-d est la longueur de la chaîne de caractère

La première rend la chaine simple à découper, à l’image de la relation de Chasles pour les vecteurs.

La seconde fournit une relation simple entre la longueur de la chaine et les index de début et de fin.

Note

Dijkstra (l’inventeur de l’algorithme utilisé dans les GPS) donne une explication détaillée de la pertinence de cette convention dans cet article.

Lorsque l’un des index est omis, il est remplacé par une valeur par défaut, dépendante du contexte (sens du parcours). Le sens du parcours est défini par les index de début d et de fin f.

Pour un parcours de gauche à droite (Left To Right : LTR), pour lequel d f :

  • la valeur par défaut de d est 0 ;

  • la valeur par défaut de f est len(s)-1 ;

  • la valeur par défaut de p est 1.

Observons quelques exemples faisant appel aux valeurs par défaut dans un contexte LTR:

>>> s[0:7] # d et f sont explicites. p est implicite
'Hello W'
>>> s[0:9:2]  # d, f et p sont explicites
'HloWr'
>>> s[:7]  # f est explicite. d et p sont implicites
'Hello W'
>>> s[6:] # d est explicite. f et p sont implicites
'World !'
>>> s[::3] # d et f sont implicites. p est explicite
'HlWl!'

A expérimenter…

Chacun des 3 index utilisés pour le slicing peut prendre une valeur explicite ou par défaut. Ce qui donne 8 possibilités. Pour le parcours LTR, 5 sont présentées ci dessus. Identifier les 3 autres cas, les mettre en oeuvre sur un exemple, conjecturer le résultat et vérifier dans l’interpréteur interactif.

Pour un parcours de droite à gauche (Right To Left : RTL), pour lequel f d :

  • la valeur par défaut de d est len(s)-1 ;

  • la valeur par défaut de f est 0 ;

  • la valeur par défaut de p est -1.

Observons quelques exemples faisant appel aux valeurs par défaut dans un contexte RTL:

>>> s[-1:-8:-1] # d, f et p sont explicites
'! dlroW'
>>> s[:-8:-1] # f et p sont explicites. d est implicite
'! dlroW'
>>> s[-9::-1] # d et p sont explicites. f est implicite
'olleH'
>>> s[::-3] # d et f sont implicites. p est explicite
'!lWlH'

A expérimenter…

Chacun des 3 index utilisés pour le slicing peut prendre une valeur explicite ou par défaut. Ce qui donne 8 possibilités. Pour le parcours RTL, 4 sont présentées ci dessus. Identifier les 4 autres cas, les mettre en oeuvre sur un exemple, conjecturer le résultat et vérifier dans l’interpréteur interactif.

Cas particulier : la syntaxe s[::-1] permet de renverser la chaine de caractère en une seule opération:

>>> s = "anticonstitutionnellement"
>>> s[::-1] # la chaîne inversée
'tnemellennoitutitsnocitna'

Les chaînes de caractère sont des séquences immutables ce qui signifie qu’on ne peut pas les modifier:

>>> s[6] = 'w'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

Il faut alors créer une autre chaîne.

>>> s2 = s[:6] + 'w' + s[7:]
>>> s2
'Hello world !'

Itérer sur une chaîne de caractères

Comme on vient de le voir, en Python les chaînes de caractères sont des séquences. Python dispose d’un opérateur commun à toutes les séquences, que l’on retrouvera par la suite. Il s’agit de l’opérateur in. Il a deux grands rôles majeurs.

Le premier est le test d’appartenance:

>>> s = "anticonstitutionnellement"
>>> 'z' in s
False
>>> 'a' in s
True

Il permet également d’itérer de façon simple sur les chaînes de caractères:

>>> s = "alpha"
>>> for c in s:
...     print(c)
...
a
l
p
h
a

L’opérateur in sera utilisé dans bien d’autres strutures de données, comme on aura l’occasion de le voir par la suite.

Les méthodes de chaîne

Tout est objet en Python et on dispose d’un grand nombre de méthodes.

Chacune de ces méthodes peut être appelée sur une chaîne de caractères : Quelques exemples:

>>> s = "anticonstitutionnellement"

>>> s.capitalize()
'Anticonstitutionnellement'

>>> s.upper()
'ANTICONSTITUTIONNELLEMENT'

>>> s.find('c')
4

Lorsque l’on doit effectuer une opération sur un objet en général, et sur des chaînes de caractères en particulier, la bonne démarche est de se documenter sur les méthodes disponibles pour l’objet considéré. Si le traitement est complexe et ne peut être réalisé à partir des méthodes fournies par le langage, alors on peut penser à implémenter ses propres fonctions, le plus souvent en se servant des méthodes built-in comme briques de base.

A expérimenter…

Mettre en oeuvre quelques méthodes de chaines de caractères pour se familiariser avec la démarche et manipuler la documentation.

Formatage

Pour un affichage optimal, il est souvent nécessaire de formatter la chaîne de caractères (str). Python dispose de la méthode format() pour réaliser cette opération. Quelques exemples très simples.

Dans le premier, la chaine de caractère est un template pour lequel les champs {0} et {1} ont une signification spéciale, et sont remplacés à l’affichage par les paramètres passés à la méthode format().

>>> print('{0} a pour capitale {1}'.format('La France', 'Paris'))
La France a pour capitale Paris

Dans le second, la chaine de caractère est un template pour lequel le champs {0:.3f} contrôle l’affichage de la valeur numérique passée en paramètre:

>>> import math
>>> print('La valeur de PI arrondie : {0:.3f}'.format(math.pi))
La valeur de PI arrondie : 3.142

La méthode format() permet de nombreuses possibilités décrites ici.

Ce qu’il faut retenir

  • Une chaine de caractère peut se délimiter avec '

  • Une chaine de caractère peut se délimiter avec "

  • Une chaine de caractère peut se délimiter avec """

  • Une chaine de caractère délimitée par ' peut inclure des sauts de ligne mais il faut utiliser un caractère spécial

  • Une chaine de caractère délimitée par " peut inclure des sauts de ligne mais il faut utiliser un caractère spécial

  • Une chaine de caractère délimitée par """ peut inclure des sauts de ligne mais il faut utiliser un caractère spécial

  • Le test d’égalité utilise l’opérateur is

  • Le test d’identité utilise l’opérateur ==

  • Pour les opérations de slicing, l’index définit le caractère

  • Pour les opérations de slicing, l’index définit l’intervalle entre les caractères

  • Pour les opérations de slicing, les index et le pas peuvent être positifs

  • Pour les opérations de slicing, les index et le pas peuvent être négatifs

  • Pour les opérations de slicing, les index et le pas peuvent être nuls

  • Pour les opérations de slicing, les index et le pas peuvent être implicites

  • Pour les opérations de slicing, si l’index est positif, on sélectionne le caractère immédiatement à gauche

  • Pour les opérations de slicing, si l’index est négatif, on sélectionne le caractère immédiatement à droite

  • Pour les opérations de slicing, si le pas est positif, on parcourt la chaine de gauche à droite (LTR)

  • Pour les opérations de slicing, si le pas est négatif, on parcourt la chaine de droite à gauche (RTL)

  • Itérer sur une chaine de caractère impose de manipuler les index

  • Itérer sur une chaine de caractère impose de manipuler les caractères

  • La bonne pratique lorsqu’on itère sur une chaine de caractère est de manipuler les caractères

  • Le formatage d’une chaine de caractère permet de contraindre sa longueur