8. Chaînes de caractères▲
Les chaînes de caractères ne sont pas comme les entiers, les flottants et les booléens. Une chaîne de caractères est une séquence, ce qui signifie que c'est une collection ordonnée d'autres valeurs. Dans ce chapitre, vous verrez comment accéder aux caractères qui composent une chaîne, et vous apprendrez à utiliser certaines des méthodes que les chaînes de caractères fournissent.
8-1. Une chaîne de caractères est une séquence▲
Une chaîne de caractères est une séquence de caractères. Vous pouvez accéder aux caractères un par un, en utilisant l'opérateur [] d'indexation :
>>>
fruit =
'banane'
>>>
lettre =
fruit[1
]
La seconde instruction sélectionne le caractère numéroté par 1 de fruit et l'affecte à lettre.
L'expression entre crochets s'appelle un indice. L'indice indique quel caractère de la séquence vous voulez (d'où son nom).
Mais il se peut que vous n'obteniez pas ce que vous attendez :
>>>
lettre
'a'
Pour la plupart des gens, la première lettre du mot 'banane' est b, et non a. Mais pour les informaticiens, l'indice est un décalage à partir du début de la chaîne, et le décalage de la première lettre est zéro.
>>>
lettre =
fruit[0
]
>>>
lettre
'b'
Donc b est la lettre numéro 0 (la « zéro-ième ») de 'banane', a est la lettre numéro 1 (« un-ième »), et n est la lettre numéro 2 (« deux-ième »).
Comme indice, vous pouvez utiliser une expression qui contient des variables et des opérateurs :
>>>
i =
1
>>>
fruit[i]
'a'
>>>
fruit[i+
1
]
'n'
Mais la valeur de l'indice doit être un entier. Sinon, vous obtenez l'erreur suivante :
>>>
lettre =
fruit[1.5
]
TypeError
: string indices must be integers
8-2. len▲
len est une fonction interne qui retourne le nombre des caractères d'une chaîne :
>>>
fruit =
'banane'
>>>
len(
fruit)
6
Pour obtenir la dernière lettre d'une chaîne de caractères, vous pourriez être tentés à écrire quelque chose comme ceci :
>>>
longueur =
len(
fruit)
>>>
dernier =
fruit[longueur]
IndexError
: string index out of range
La raison de IndexError est qu'il n'y a aucune lettre dans 'banane' ayant l'indice 6. Puisque nous avons commencé à compter à partir de zéro, les six lettres sont numérotées de 0 à 5. Pour obtenir le dernier caractère, vous devez soustraire 1 de longueur :
>>>
dernier =
fruit[longueur -
1
]
>>>
dernier
'e'
Ou vous pouvez utiliser des indices négatifs, qui comptent à l'envers à partir de la fin de la chaîne. L'expression fruit[-1] donne la dernière lettre, fruit[-2] donne l'avant-dernière, et ainsi de suite.
8-3. Parcours avec une boucle for▲
Beaucoup de calculs impliquent le traitement d'une chaîne caractère par caractère. Souvent, ils commencent au début, sélectionnent chaque caractère à tour de rôle, lui font quelque chose et continuent jusqu'à la fin. Ce modèle de traitement est appelé un parcours. Une façon d'écrire un tel parcours est une boucle while :
index =
0
while
index <
len(
fruit):
lettre =
fruit[index]
print
(
lettre)
index =
index +
1
Cette boucle parcourt la chaîne et affiche chaque lettre sur une ligne individuelle. La condition de la boucle est index < len(fruit), donc lorsque l'indice devient égal à la longueur de la chaîne, la condition devient fausse, et le corps de la boucle ne s'exécute plus. Le dernier caractère accédé est celui d'indice len(fruit) -1, qui est le dernier caractère de la chaîne.
À titre d'exercice, écrivez une fonction qui prend une chaîne comme argument et affiche les lettres à partir de la fin, une par ligne.
Une autre façon d'écrire un parcours est d'utiliser une boucle for :
for
lettre in
fruit:
print
(
lettre)
À chaque passage dans la boucle, le caractère suivant dans la chaîne est affecté à la variable lettre. La boucle continue jusqu'à ce qu'il n'y ait aucun caractère restant.
L'exemple suivant montre comment utiliser la concaténation (addition de chaînes de caractères) et une boucle pour générer une série dans l'ordre alphabétique. Dans le livre de Robert McCloskey, Faire place aux canetons, les noms des canetons sont Jack, Kack, Lack, Mack, Nack, Ouack, Pack, et Quack. Cette boucle affiche ces noms dans l'ordre :
prefixes =
'JKLMNOPQ'
suffixe =
'ack'
for
lettre in
prefixes:
print
(
lettre +
suffixe)
L'affichage sera :
Jack
Kack
Lack
Mack
Nack
Oack
Pack
Qack
Bien sûr, ce n'est pas tout à fait correct parce que « Ouack » et « Quack » sont mal orthographiés. À titre d'exercice, modifiez le programme pour corriger cette erreur.
8-4. Tranches de chaînes de caractères▲
Un segment de chaîne de caractères s'appelle une tranche. La sélection d'une tranche est similaire à la sélection d'un caractère :
>>>
s =
'Monty Python'
>>>
s[0
:5
]
'Monty'
>>>
s[6
:12
]
'Python'
L'opérateur [n:m] retourne la partie de la chaîne allant du « n-ième » caractère, y compris celui-ci, au « m-ième » caractère, en excluant ce dernier. Ce comportement est contre-intuitif, mais si vous imaginez les indices comme pointant entre les caractères, comme sur la Figure 8.1, cela peut vous aider.
Si vous omettez le premier indice (avant le deux-points), la tranche commence au début de la chaîne. Si vous omettez le second indice, la tranche s'étend jusqu'à la fin de la chaîne :
>>>
fruit =
'banane'
>>>
fruit[:3
]
'ban'
>>>
fruit[3
:]
'ane'
Si le premier indice est supérieur ou égal au second, le résultat est une chaîne vide, représentée par deux guillemets simples :
>>>
fruit =
'banane'
>>>
fruit[3
:3
]
''
Une chaîne vide ne contient aucun caractère et a une longueur de 0, mais à part ça, c'est une chaîne comme toutes les autres.
Poursuivant cet exemple, que pensez-vous que fruit[:] représente ? Essayez et vous verrez.
8-5. Les chaînes de caractères sont immuables▲
Il est tentant d'utiliser l'opérateur [] du côté gauche de l'affectation, avec l'intention de modifier un caractère dans une chaîne. Par exemple :
>>>
salutation =
'Hello, world!'
>>>
salutation[0
] =
'J'
TypeError
: 'str'
object does not
support item assignment
Dans ce cas-ci, l'« objet » est la chaîne de caractères et l'« élément » (item) est le caractère que vous avez essayé d'affecter. Pour l'instant, considérez qu'un objet est la même chose qu'une valeur, mais nous allons affiner cette définition plus tard (section 10.10Objets et valeurs).
La raison de l'erreur est que les chaînes de caractères sont immuables (on dit aussi non mutables), ce qui signifie que vous ne pouvez pas modifier une chaîne existante. Le mieux que vous puissiez faire est de créer une nouvelle chaîne qui est une variation sur l'original :
>>>
salutation =
'Hello, world!'
>>>
nouvelle_salutation =
'J'
+
salutation[1
:]
>>>
nouvelle_salutation
'Jello, world!'
Cet exemple concatène une nouvelle première lettre sur une tranche de salutation. Il n'a aucun effet sur la chaîne d'origine.
8-6. Recherche▲
Que fait la fonction suivante ?
def
trouver
(
mot, lettre):
index =
0
while
index <
len(
mot):
if
mot[index] ==
lettre:
return
index
index =
index +
1
return
-
1
Dans un sens, trouver est l'inverse de l'opérateur []. Au lieu de prendre un indice et d'extraire le caractère correspondant, elle prend un caractère et trouve l'indice où le caractère apparaît. Si le caractère n'est pas trouvé, la fonction retourne -1.
Celui-ci est le premier exemple d'une instruction return dans une boucle. Si mot[index] == lettre, la fonction sort de la boucle et retourne immédiatement.
Si le caractère n'existe pas dans la chaîne, le programme termine la boucle normalement et retourne -1.
Ce modèle de calcul - parcourir une séquence et retourner quand nous trouvons ce que nous cherchons - s'appelle une recherche.
À titre d'exercice, modifiez trouver de sorte qu'elle prenne un troisième paramètre, l'indice dans mot où elle devrait commencer la recherche.
8-7. Boucler et compter▲
Le programme suivant compte les occurrences de la lettre a dans une chaîne de caractères :
mot =
'banane'
compteur =
0
for
lettre in
mot:
if
lettre ==
'a'
:
compteur =
compteur +
1
print
(
compteur)
Ce programme montre un autre modèle de calcul appelé un compteur. La variable compteur est initialisée à 0 et ensuite elle est incrémentée chaque fois qu'un a est trouvé. Lorsque la boucle se termine, compteur contient le résultat - le nombre total de lettres a.
À titre d'exercice, encapsulez ce code dans une fonction nommée compteur et généralisez-le pour qu'il accepte la chaîne de caractères et la lettre comme arguments.
Puis réécrivez la fonction de sorte qu'au lieu de parcourir la chaîne, elle utilise la version à trois paramètres de trouver de la section précédente.
8-8. Méthodes de chaînes de caractères▲
Les chaînes de caractères fournissent des méthodes qui effectuent une variété d'opérations utiles. Une méthode est semblable à une fonction - elle prend des arguments et renvoie une valeur -, mais la syntaxe d'appel est différente. Par exemple, la méthode upper prend une chaîne de caractères et retourne une nouvelle chaîne avec toutes les lettres en majuscule.
Au lieu de la syntaxe de fonction upper(mot), elle utilise la syntaxe de méthode, mot.upper().
>>>
mot =
'banane'
>>>
nouveau_mot =
mot.upper
(
)
>>>
nouveau_mot
'BANANE'
Cette forme de notation pointée indique le nom de la méthode, upper, et le nom de la chaîne sur laquelle on applique la méthode, mot. Les parenthèses vides indiquent que cette méthode ne prend aucun argument.
Un appel de méthode est appelé une invocation ; dans ce cas, nous dirions que nous invoquons upper sur mot.
Il se trouve qu'il existe une méthode de chaîne de caractères nommée find, qui est remarquablement similaire à la fonction trouver que nous avons écrite :
>>>
mot =
'banane'
>>>
index =
mot.find
(
'a'
)
>>>
index
1
Dans cet exemple, nous invoquons find sur mot et passons la lettre que nous recherchons en tant que paramètre.
En fait, la méthode find est plus générale que notre fonction ; elle peut trouver des sous-chaînes, et pas seulement des caractères :
>>>
mot.find
(
'na'
)
2
Par défaut, find commence sa recherche au début de la chaîne, mais elle peut prendre un deuxième argument, l'indice où elle devrait commencer à chercher :
>>>
mot.find
(
'ne'
, 3
)
4
Cela est un exemple d'un argument optionnel ; find peut également prendre un troisième argument, l'indice où elle doit s'arrêter :
>>>
nom =
'bob'
>>>
nom.find
(
'b'
, 1
, 2
)
-
1
Cette recherche échoue parce que b n'apparaît pas dans la plage d'indices de 1 à 2, la lettre correspondant à l'indice 2 étant non comprise. En recherchant jusqu'à cette valeur d'indice, mais sans inclure le caractère correspondant, le second indice permet à find de rester cohérente avec l'opérateur de tranche.
8-9. L'opérateur in▲
Le mot in est un opérateur booléen qui prend deux chaînes de caractères et retourne True si la première apparaît comme une sous-chaîne dans la seconde :
>>>
'a'
in
'banane'
True
>>>
'graine'
in
'banane'
False
Par exemple, la fonction suivante affiche toutes les lettres de mot1 qui apparaissent aussi dans mot2 :
def
lettres_communes
(
mot1, mot2):
for
lettre in
mot1:
if
lettre in
mot2:
print
(
lettre)
Avec des noms de variables bien choisis, Python peut parfois être lu comme si c'était une phrase écrite en anglais. Vous pouvez lire cette boucle ainsi : « for (chaque) lettre in (le premier) mot, if (la) lettre (apparaît) in (le deuxième) mot, print (la) lettre ».
Voici ce que vous obtenez si vous comparez des pommes et des oranges :
>>>
lettres_communes
(
'pommes'
, 'oranges'
)
o
e
s
8-10. Comparaison de chaînes de caractères▲
Les opérateurs relationnels peuvent être utilisés sur les chaînes de caractères. Pour vérifier si deux chaînes sont égales :
if
mot ==
'banane'
:
print
(
"C'est bon, des bananes."
)
D'autres opérations relationnelles sont utiles pour mettre des mots dans l'ordre alphabétique :
if
mot <
'banane'
:
print
(
'Votre mot, '
+
mot +
', vient avant banane.'
)
elif
mot >
'banane'
:
print
(
'Votre mot, '
+
mot +
', vient après banane.'
)
else
:
print
(
"C'est bon, des bananes."
)
Python ne gère pas les lettres majuscules et minuscules de la même façon que la plupart des gens. Toutes les lettres majuscules viennent avant toutes les lettres minuscules, donc :
Votre mot, Pomme, vient avant banane.
Une façon courante de résoudre ce problème est de convertir les chaînes dans un format standard, par exemple des minuscules, avant d'effectuer la comparaison. Gardez cela à l'esprit pour le cas où l'on désire vous faire croquer la Pomme.
8-11. Débogage▲
Lorsque vous utilisez les indices pour parcourir les valeurs dans une séquence, il est délicat d'obtenir correctement le début et la fin du parcours. Voici une fonction qui est censée comparer deux mots et retourner True si l'un des mots est l'inverse de l'autre, mais elle contient deux erreurs :
def
is_inverse
(
mot1, mot2):
if
len(
mot1) !=
len(
mot2):
return
False
i =
0
j =
len(
mot2)
while
j >
0
:
if
mot1[i] !=
mot2[j]:
return
False
i =
i+
1
j =
j-
1
return
True
La première instruction if vérifie si les mots ont la même longueur. Si ce n'est pas le cas, nous pouvons renvoyer False immédiatement. Autrement, pour le reste de la fonction, on peut supposer que les mots ont la même longueur. Ceci est un exemple du mécanisme de garde vu dans la section 6.8Vérifier les types.
i et j sont des indices : i parcourt mot1 à partir du début tandis que j parcourt mot2 à partir de la fin. Si nous trouvons deux lettres qui ne correspondent pas, nous pouvons renvoyer False immédiatement. Si nous arrivons à la fin de la boucle et toutes les lettres correspondent, nous renvoyons True.
Si nous testons cette fonction avec les mots « tracé » et « écart », nous nous attendons à ce que la valeur de retour soit True, mais nous obtenons une IndexError :
>>>
is_inverse
(
'tracé'
, 'écart'
)
...
File "reverse.py"
, line 15
, in
is_inverse
if
mot1[i] !=
mot2[j]:
IndexError
: string index out of range
Pour déboguer ce genre d'erreur, mon premier réflexe est d'imprimer les valeurs des indices immédiatement avant la ligne où l'erreur apparaît.
while
j >
0
:
print
(
i, j) # affichage ici
if
mot1[i] !=
mot2[j]:
return
False
i =
i+
1
j =
j-
1
Maintenant, quand j'exécute à nouveau le programme, je reçois plus d'informations :
>>>
is_inverse
(
'tracé'
, 'écart'
)
0
5
...
IndexError
: string index out of range
Au premier passage dans la boucle, la valeur de j est 5, ce qui dépasse la taille de la chaîne 'tracé'. L'indice du dernier caractère est 4, donc la valeur initiale de j devrait être len(mot2) - 1.
Si je corrige cette erreur et j'exécute à nouveau le programme, j'obtiens :
>>>
is_inverse
(
tracé', '
écart')
0 4
1 3
2 2
3 1
True
Cette fois, nous obtenons la bonne réponse, mais il semble que la boucle n'a été exécutée que quatre fois, ce qui est suspect. Pour avoir une meilleure idée de ce qui se passe, il est utile de dessiner un diagramme d'état. Au cours de la première itération, la structure de pile de is_inverse est représentée sur la figure 8.2.
J'ai arrangé les variables dans la structure et ajouté des lignes pointillées pour montrer que les valeurs de i et j indiquent des caractères dans mot1 et mot2.
Avec ce schéma comme point de départ, simulez sur papier l'exécution de ce programme, en modifiant les valeurs de i et j lors de chaque itération. Trouvez et corrigez la seconde erreur dans cette fonction.
8-12. Glossaire▲
- objet : quelque chose auquel une variable peut se référer. Pour l'instant, vous pouvez utiliser « objet » et « valeur » de façon interchangeable.
- séquence : une collection ordonnée de valeurs où chaque valeur est identifiée par un indice entier.
- élément : l'une des valeurs dans une séquence.
- indice : une valeur entière utilisée pour sélectionner un élément dans une séquence, comme un caractère dans une chaîne. En Python, les indices commencent à 0.
- tranche : une partie d'une chaîne spécifiée par une gamme d'indices.
- chaîne vide : une chaîne sans aucun caractère et de longueur 0, représentée par deux guillemets simples.
- immuable : la propriété d'une séquence dont les éléments ne peuvent être modifiés.
- parcourir : itérer à travers les éléments dans une séquence, en exécutant une opération du même type sur chacun.
- recherche : un modèle de parcours qui s'arrête quand il trouve ce qu'il recherche.
- compteur : une variable utilisée pour compter quelque chose, généralement initialisée à zéro, puis incrémentée.
- invocation : une instruction qui appelle une méthode.
- argument optionnel : un argument de fonction ou de méthode qui n'est pas requis.
8-13. Exercices▲
Exercice 1
Lisez la documentation des méthodes de chaînes de caractères sur http://docs.python.org/3/library/stdtypes.html#string-methods . Vous voudrez peut-être expérimenter certaines d'entre elles pour vous assurer que vous comprenez comment elles fonctionnent. strip et replace sont particulièrement utiles.
La documentation utilise une syntaxe qui pourrait être source de confusion. Par exemple, dans find (sub[, start[, end]]) , les parenthèses indiquent des arguments optionnels. Donc sub est requis, mais start est optionnel, et si vous incluez start , alors end est optionnel.
Exercice 2
Il existe une méthode de chaîne de caractères appelée count qui est similaire à la fonction de la section 8.7Boucler et compter . Lisez la documentation de cette méthode et écrivez une invocation qui compte le nombre des a dans 'banane' .
Exercice 3
Une tranche de chaîne peut prendre un troisième indice qui spécifie la « taille du pas » ; autrement dit, le nombre d'espaces entre caractères successifs. Une taille de pas de 2 signifie un caractère sur deux ; 3 signifie un caractère sur trois, etc.
>>>
fruit =
'banane'
>>>
fruit[0
:5
:2
]
'bnn'
Une taille de pas de -1 parcourt le mot à l'envers, donc la tranche [:: - 1] génère une chaîne inversée.
Utilisez cette syntaxe pour écrire une version d'une ligne de la méthode is_palindrome de l'exercice 3 du chapitre 6.
Exercice 4
Les fonctions suivantes sont toutes censées vérifier si une chaîne contient au moins une lettre minuscule quelconque, mais au moins certaines d'entre elles sont erronées. Pour chaque fonction, décrivez ce que fait réellement la fonction (en supposant que le paramètre est une chaîne).
def
trouver_minuscules1
(
s):
for
c in
s:
if
c.islower
(
):
return
True
else
:
return
False
def
trouver_minuscules2
(
s):
for
c in
s:
if
'c'
.islower
(
):
return
'True'
else
:
return
'False'
def
trouver_minuscules3
(
s):
for
c in
s:
drapeau =
c.islower
(
)
return
drapeau
def
trouver_minuscules4
(
s):
drapeau =
False
for
c in
s:
drapeau =
drapeau or
c.islower
(
)
return
drapeau
def
trouver_minuscules5
(
s):
for
c in
s:
if
not
c.islower
(
):
return
False
return
True
Exercice 5
Un chiffre de César est une forme faible de chiffrement qui implique la « rotation » de chaque lettre d'un nombre fixe de places. La rotation d'une lettre signifie de décaler sa place dans l'alphabet, en repassant par le début si nécessaire, de sorte qu'après la rotation par 3, 'A' devient 'D' et 'Z' décalé de 1 est 'A'.
Pour effectuer la rotation d'un mot, décalez-en chaque lettre par le même nombre. Par exemple, les mots « JAPPA » et « ZAPPA », décalés de quatre lettres, donnent respectivement « DETTE » et « NETTE », et le mot « RAVIER », décalé de 13 crans, donne « ENIVRE » (et réciproquement). De même, le mot « OUI » décalé de 10 crans devient « YES ». Dans le film 2001 : l'Odyssée de l'espace, l'ordinateur du vaisseau s'appelle HAL, qui est IBM décalé de -1.
Écrivez une fonction appelée rotate_word qui prend comme paramètres une chaîne de caractères et un entier, et renvoie une nouvelle chaîne qui contient les lettres de la chaîne d'origine décalées selon le nombre donné.
Vous voudrez peut-être utiliser la fonction interne ord , qui convertit un caractère en un code numérique, et chr , qui convertit des codes numériques en caractères. Les lettres de l'alphabet sont codées dans l'ordre alphabétique, par exemple :
parce que 'c' est la « deux-ième » lettre de l'alphabet. Mais attention : les codes numériques pour les lettres majuscules et minuscules sont différents. Et évitez les mots contenant des caractères accentués, cédilles, apostrophes et autres traits d'union (NdT).
Des blagues potentiellement offensantes sur l'Internet sont parfois encodées en ROT13, qui est un chiffre de César avec un décalage de 13. (Comme l'alphabet latin comprend 26 lettres, appliquer à deux reprises un décalage de 13 lettres redonne le texte d'origine, ce qui fait que le même programme peut servir à chiffrer un texte en clair et à déchiffrer le cryptogramme résultant - NdT). Si vous ne vous offusquez pas facilement, trouvez et décodez certains d'entre eux. Solution : rotate.py .