Leçon 01-3 — Les chaînes de caractères en profondeur
1. Objectif
À la fin de cette leçon tu sauras : accéder à n'importe quel caractère d'une chaîne par sa position (positive ou négative), en extraire des morceaux avec le slicing, mesurer avec len(), transformer avec les méthodes essentielles (upper, lower, strip, replace…), chercher dedans (in, find, count, startswith), et produire des affichages alignés avec les f-strings ({x:.2f}, {x:>10}). Tu sauras aussi expliquer pourquoi une chaîne ne se modifie jamais — et éviter les deux bugs que cette règle provoque.
2. Pourquoi c'est important
Presque toutes les données que ton programme reçoit arrivent en texte : la saisie d'un input(), une ligne de fichier CSV (⏩ niveau 9), une réponse web, un nom de fichier. Avant de les utiliser, il faut les nettoyer (espaces parasites, majuscules incohérentes) et les découper (extraire l'année d'une date, le domaine d'un email). C'est exactement ce que fait cette leçon. Les outils vus ici — indexation, slicing, méthodes — reviendront à l'identique au niveau 3 sur les listes : tout ce que tu apprends ici, tu l'apprends deux fois.
3. Explication simple
Une chaîne, c'est un train de caractères : chaque caractère a son wagon, et chaque wagon a un numéro. La numérotation commence à 0, pas à 1 :
P y t h o n
0 1 2 3 4 5 ← indices positifs (depuis la gauche)
-6 -5 -4 -3 -2 -1 ← indices négatifs (depuis la droite)
word[0]→ le premier caractère.word[-1]→ le dernier.word[2:5]→ une tranche (slice) : les wagons 2, 3, 4 — le 5 est exclu.len(word)→ le nombre de wagons.
Une chaîne sait aussi faire des choses : elle a des méthodes, qu'on appelle avec un point : word.upper() fabrique la version en majuscules, word.strip() fabrique la version sans espaces autour.
Le mot important est fabrique : une chaîne est immuable — on ne change jamais ses wagons. Toute transformation crée un nouveau train, et c'est à toi de le récupérer dans une variable.
4. Explication approfondie
a) Indexation : les deux sens, et la sortie de route.
Un indice positif compte depuis la gauche à partir de 0 ; un indice négatif compte depuis la droite à partir de -1 (word[-1] est le dernier caractère, très utilisé). Les indices valides de "Python" vont de 0 à 5 et de -1 à -6. En dehors : IndexError: string index out of range. Piège classique : le dernier caractère est word[len(word) - 1], pas word[len(word)] — la numérotation à partir de 0 décale tout d'un cran.
b) Slicing : [start:stop:step], et la règle du stop exclu.
word[start:stop] prend du wagon start inclus au wagon stop exclu. Cette exclusion paraît bizarre mais elle est cohérente : word[0:3] contient exactement 3 caractères (stop - start), et word[:3] + word[3:] recollent la chaîne entière sans doublon. Chaque borne est optionnelle :
word[:3]→ du début au wagon 2 ;word[3:]→ du wagon 3 à la fin.word[-3:]→ les 3 derniers caractères.- Le troisième nombre est le pas :
word[::2]→ un caractère sur deux ;word[::-1]→ la chaîne à l'envers (pas de -1 : on recule).
Contrairement à l'indexation, le slicing ne plante jamais : word[2:50] s'arrête gentiment à la fin, word[5:2] donne une chaîne vide "". C'est confortable — et sournois : une borne fausse ne produit aucune erreur, juste un résultat faux. Vérifie ta sortie.
c) Immutabilité : la règle qui gouverne toute la leçon.
Une chaîne ne se modifie jamais en place. Deux conséquences concrètes :
word = "python"
word[0] = "P" # TypeError: 'str' object does not support item assignment
et — bien plus fréquent car silencieux :
message = "bonjour"
message.upper() # fabrique "BONJOUR"... et le jette aussitôt !
print(message) # bonjour ← inchangé
Toute méthode de chaîne retourne une nouvelle chaîne ; l'originale reste intacte. Le réflexe : toujours récupérer le résultat — message = message.upper(). Ce n'est pas une bizarrerie gratuite : une valeur immuable ne peut pas changer dans ton dos, ce qui rend les programmes plus prévisibles. Au niveau 3, tu rencontreras les listes, qui sont mutables — et le contraste te fera comprendre pourquoi c'est un vrai sujet.
d) Les méthodes : des fonctions attachées à la valeur.
upper() se colle après la valeur avec un point : word.upper(). Les parenthèses sont obligatoires — sans elles, tu obtiens la méthode elle-même, pas son résultat (bug silencieux, voir section 6). Comme chaque méthode retourne une chaîne, on peut enchaîner : raw.strip().lower() se lit de gauche à droite (d'abord strip, puis lower sur le résultat). L'ordre compte — tu le vérifieras en exercice.
Les indispensables : upper() / lower() (casse), strip() (enlève espaces et retours à la ligne aux deux bouts, pas au milieu), replace(old, new) (remplace toutes les occurrences), startswith(prefix) (retourne un bool), count(sub) (nombre d'occurrences), find(sub) (position de la première occurrence, ou -1 si absent — jamais d'erreur, c'est un piège, voir section 6).
e) Chercher : in d'abord, find si tu as besoin de la position.
"py" in sentence retourne True/False (un bool, vu en 01-2) et se lit comme de l'anglais : c'est l'outil par défaut. find sert uniquement quand tu veux où — pour découper ensuite avec un slice. Tout est sensible à la casse : "Py" in "python" est False ; d'où le motif classique ma_chaine.lower() avant de chercher.
f) split et join — aperçu honnête.
"a,b,c".split(",") découpe en liste ['a', 'b', 'c'], et "-".join(liste) recolle. ⏩ Les listes sont le grand sujet du niveau 3 : ici je te les montre une fois parce que split/join sont inséparables des chaînes, mais on ne s'en servira pas dans les exercices de cette leçon.
g) f-strings avancées : le mini-langage de format.
Après les deux-points dans {...}, tu décris l'affichage : {x:.2f} (2 décimales, vu en 01-2), {x:>10} (aligné à droite sur 10 caractères), {x:<10} (à gauche), {x:^10} (centré). Combinables : {price:>8.2f} = « 2 décimales, aligné à droite sur 8 ». C'est l'outil des tableaux propres dans le terminal — le mini-projet t'en fera faire un.
Note sur les boucles : parcourir une chaîne caractère par caractère se fait avec for — ⏩ c'est LE sujet du niveau 2 ; je l'évite entièrement ici car len, count, in et le slicing couvrent tous nos besoins sans boucle, et tu apprendras le parcours proprement là-bas.
Difficulté honnête : cette leçon est dense — deux vrais pièges conceptuels (le stop exclu du slicing, l'immutabilité) et beaucoup de vocabulaire de méthodes. Prévois deux sessions : sections 1–7 en tapant chaque exemple, puis les exercices. Les méthodes ne s'apprennent pas par cœur : elles rentrent par les doigts.
5. Exemples commentés
Exemple 1 — indexation et len()
word = "Python"
print(word[0]) # P ← premier caractère : indice 0, pas 1 !
print(word[5]) # n ← 6 caractères, donc dernier indice = 5
print(word[-1]) # n ← -1 : le dernier, sans connaître la longueur
print(word[-6]) # P ← -6 : le premier, en comptant depuis la droite
print(len(word)) # 6
print(word[len(word) - 1]) # n ← formule générale du dernier caractère
Exemple 2 — slicing dans tous les sens
word = "Python"
print(word[0:2]) # Py ← indices 0 et 1 : le 2 est EXCLU
print(word[2:]) # thon ← de l'indice 2 à la fin
print(word[:3]) # Pyt ← du début à l'indice 2
print(word[-3:]) # hon ← les 3 derniers caractères
print(word[::2]) # Pto ← un caractère sur deux (pas de 2)
print(word[::-1]) # nohtyP ← pas de -1 : la chaîne à l'envers
print(word[2:50]) # thon ← borne trop grande : pas d'erreur, ça s'arrête
Exemple 3 — nettoyer : strip, upper, lower, replace
raw = " Bonjour Python "
print(raw.strip()) # Bonjour Python ← espaces des bouts enlevés
print(raw.strip().upper()) # BONJOUR PYTHON ← enchaînement : strip PUIS upper
print(raw.strip().lower()) # bonjour python
print(raw.replace("Bonjour", "Hello")) # Hello Python ← les espaces restent :
# replace ne touche que ce qu'on lui demande
Chaque ligne fabrique une nouvelle chaîne : raw n'a pas bougé d'un caractère entre la première et la dernière ligne.
Exemple 4 — chercher : in, startswith, count, find
sentence = "le python est un langage, le python est lisible"
print("python" in sentence) # True ← le test d'appartenance, outil par défaut
print("java" in sentence) # False
print(sentence.startswith("le")) # True ← sensible à la casse ("Le" → False)
print(sentence.count("python")) # 2 ← nombre d'occurrences
print(sentence.find("est")) # 10 ← position de la PREMIÈRE occurrence
print(sentence.find("java")) # -1 ← absent : -1, PAS une erreur
print(sentence[10:13]) # est ← find + slice : le duo qui découpe
Exemple 5 — split et join (aperçu ⏩ niveau 3)
csv_line = "ada,1815,london"
parts = csv_line.split(",") # découpe sur les virgules → une LISTE
print(parts) # ['ada', '1815', 'london']
print(parts[0]) # ada ← l'indexation marche aussi sur les listes !
print("-".join(parts)) # ada-1815-london ← recolle avec le séparateur choisi
⏩ Les listes ([...]) sont le cœur du niveau 3. Retiens juste l'existence du duo split/join — tu le réclameras bientôt.
Exemple 6 — f-strings avancées : un tableau aligné
print(f"{'article':<12}{'prix':>8}") # texte à gauche sur 12, à droite sur 8
print(f"{'café':<12}{1.9:>8.2f}") # >8.2f : 2 décimales, calé à droite sur 8
print(f"{'sandwich':<12}{4.5:>8.2f}")
print(f"{'total':<12}{6.4:>8.2f}")
Sortie :
article prix
café 1.90
sandwich 4.50
total 6.40
Remarque : dans {'article':<12}, la valeur formatée est une chaîne littérale entre apostrophes — obligatoire ici, car la f-string elle-même utilise déjà les guillemets doubles.
6. Erreurs fréquentes
1) Sortir du train : indice hors limites
word = "Python"
print(word[6])
# IndexError: string index out of range
6 caractères → indices 0 à 5. word[6] n'existe pas, et word[len(word)] non plus (c'est la même erreur déguisée). Le dernier caractère est word[len(word) - 1], ou plus simplement word[-1].
2) Essayer de modifier une chaîne
word = "python"
word[0] = "P"
# TypeError: 'str' object does not support item assignment
Les chaînes sont immuables : on ne remplace pas un wagon. Correction : on fabrique la bonne chaîne — word = "P" + word[1:] (ou word.replace(...), ou word.capitalize() selon le besoin).
3) Appeler une méthode et jeter le résultat
message = "bonjour"
message.upper() # fabrique "BONJOUR"... que personne ne récupère
print(message) # bonjour ← inchangé, aucun message d'erreur
LE bug silencieux de cette leçon. upper() retourne une nouvelle chaîne ; sans assignation, elle disparaît. Correction : message = message.upper().
4) Oublier les parenthèses de la méthode
word = "trading"
big = word.upper # ← il manque les ()
print(f"Majuscules : {big}")
# Majuscules : <built-in method upper of str object at 0x0000018A1C1D0F00>
Sans parenthèses, tu obtiens la méthode elle-même (l'adresse hexadécimale varie à chaque exécution), pas son résultat. Pas de plantage : le programme « marche » et affiche du charabia. Dès que tu vois <built-in method ...> dans ta sortie, cherche les parenthèses manquantes.
5) Utiliser le -1 de find() comme un indice
s = "abc"
position = s.find("z") # "z" absent → -1, sans erreur
print(s[position]) # c ← s[-1] est VALIDE : le dernier caractère !
Double piège : find ne plante jamais (il retourne -1), et -1 est un indice légal (le dernier caractère). Résultat : aucun message, juste une valeur fausse qui se propage. Réflexe : après un find, demande-toi toujours « et si c'est -1 ? » (⏩ le if du niveau 2 permettra de s'en protéger).
6) Confondre fonction et méthode : .len() n'existe pas
word = "python"
print(word.len())
# AttributeError: 'str' object has no attribute 'len'
len() est une fonction : elle enveloppe la valeur — len(word). Les méthodes (word.upper()) se collent après avec un point. AttributeError signifie « cette valeur n'a pas de méthode portant ce nom » — souvent une faute de frappe (word.uper()) ou une confusion fonction/méthode comme ici.
7. Lire les messages d'erreur
Les deux exceptions emblématiques de cette leçon. Toujours de bas en haut.
IndexError — l'indice hors limites
Traceback (most recent call last):
File "c:\Users\jtron\Claude\learnpython\badge.py", line 2, in <module>
print(word[6])
~~~~^^^
IndexError: string index out of range
- Dernière ligne :
IndexError= « tu as demandé un wagon qui n'existe pas ». Le message est toujours le même pour les chaînes :string index out of range. - Au-dessus : fichier
badge.py, ligne 2, et le code fautif — les^^^soulignent le[6], les~~~~la chaîne indexée. - Réflexe : affiche
len(word)juste avant la ligne fautive. Neuf fois sur dix, c'est un décalage de 1 (len(word)au lieu delen(word) - 1) ou une chaîne plus courte que prévu (vide, par exemple — uninput()où l'utilisateur a juste appuyé sur Entrée a une longueur de 0, et TOUT indice y est hors limites).
TypeError — la modification interdite
Traceback (most recent call last):
File "c:\Users\jtron\Claude\learnpython\fix.py", line 2, in <module>
word[0] = "P"
~~~~^^^
TypeError: 'str' object does not support item assignment
- Dernière ligne :
TypeError, comme en 01-2 (« opération interdite pour ce type ») — mais le message est nouveau :does not support item assignment= « on n'assigne pas dans les cases d'unstr». C'est l'immutabilité qui te parle. - Au-dessus : la ligne exacte, avec le
[0]souligné. - Réflexe : ne cherche pas à contourner — reformule. Tu ne modifies pas une chaîne, tu en construis une nouvelle (slicing + concaténation, ou
replace), puis tu réassignes la variable.
Et rappelle-toi la section 6 : les pires bugs de cette leçon (strip() non assigné, parenthèses oubliées, find → -1) ne produisent aucun traceback. Quand la sortie est fausse sans erreur, relis tes lignes de transformation : le résultat est-il récupéré quelque part ?
8. Exercices — faciles
Règles : nouveau fichier par exercice dans
lessons/level-01-bases/exercices/mes-reponses/, nomméex-01-3-a.py, etc. Tape tout à la main. Prédis la sortie AVANT d'exécuter.
a) Sans exécuter, prédis la sortie de chaque ligne (la dernière est piégée), puis vérifie :
word = "trading"
print(word[0])
print(word[-1])
print(word[3])
print(len(word))
print(word[7])
b) La variable date = "2026-07-05" est au format année-mois-jour. Extrais l'année, le mois et le jour avec trois slices, puis affiche en f-string : Le 05/07/2026. Contrainte : interdiction de retaper les nombres en dur.
c) Crée full = " jean DUPONT " (avec les espaces). Affiche : Avant : 15 caractères — après : 11 caractères puis Fiche : JEAN DUPONT. Contraintes : strip(), upper() et len() obligatoires ; la version nettoyée est stockée dans une variable (pas re-calculée à chaque ligne).
d) Toujours avec word = "trading", prédis puis vérifie :
print(word[:4])
print(word[4:])
print(word[::-1])
print(word[2:50])
print(word[5:2])
9. Exercices — moyens
a) Ce programme est censé afficher Majuscules : TRADING puis Dernière lettre : g, mais aucune des deux lignes ne sort correctement. Trouve les DEUX bugs sans l'exécuter (un silencieux, un qui plante), puis corrige :
word = "trading"
big = word.upper
print(f"Majuscules : {big}")
print(f"Dernière lettre : {word[len(word)]}")
b) Demande le prénom puis le nom (input() × 2, l'utilisateur peut taper en minuscules). Affiche les initiales au format A. L. — première lettre de chaque, en majuscule, suivie d'un point. Contraintes : indexation + upper() ; doit marcher quel que soit ce que tape l'utilisateur (ada → A).
c) Un « slug » est un titre transformé en identifiant d'URL. Écris un programme qui transforme title = "Mon Super Article !" en mon-super-article : supprimer le !, enlever les espaces des bouts, tout en minuscules, espaces restants remplacés par des tirets. Tout tient en UNE chaîne d'appels de méthodes — mais attention, l'ordre des maillons change le résultat : trouve un ordre qui marche, et note dans ton journal un ordre qui échoue et pourquoi (indice : que devient l'espace laissé par le ! supprimé ?).
10. Exercices — difficiles
a) Un palindrome se lit dans les deux sens (kayak, Engage le jeu que je le gagne). Demande une phrase à l'utilisateur et affiche True si c'en est un, False sinon — en ignorant majuscules et espaces. ⏩ == (vu en 01-2, section 4a) produit exactement ce True/False ; pas besoin de if.
- Indice 1 : compare la chaîne à sa version retournée. Comment retourne-t-on une chaîne en un seul slice ?
- Indice 2 :
"Kayak"≠"kayaK"tant que la casse et les espaces traînent. Nettoie AVANT de comparer — deux méthodes vues en section 5. - Indice 3 :
cleaned = phrase.lower().replace(" ", ""), puis comparecleanedàcleaned[::-1].
b) Sans split() : demande une adresse email (ex. ada.lovelace@example.com) et affiche trois lignes — l'utilisateur (ada.lovelace), le domaine (example.com), et l'extension (com). Contraintes : find() + slicing uniquement ; aucune position tapée en dur (ça doit marcher pour bob@x.fr aussi).
- Indice 1 : la position du
@est la clé de tout. Quelle méthode te la donne ? - Indice 2 : l'utilisateur est « tout ce qui est avant le
@» :email[:position]. Le domaine : tout ce qui est après — attention au+ 1pour ne pas embarquer le@lui-même. - Indice 3 : pour l'extension, rejoue exactement le même motif (
findd'un., puis slice) sur la chaîne du domaine, pas sur l'email entier.
11. Mini-projet lié — « nettoyeur de texte »
Écris cleaner.py : l'utilisateur tape une phrase (souvent mal fichue — espaces autour, casse incohérente) et le programme produit une fiche d'analyse alignée.
Comportement attendu (exemple de session) :
=== Nettoyeur de texte ===
Ta phrase ? le Python est un langage GÉNIAL
Originale : [ le Python est un langage GÉNIAL ]
Nettoyée : [le Python est un langage GÉNIAL]
MAJUSCULES : LE PYTHON EST UN LANGAGE GÉNIAL
minuscules : le python est un langage génial
Inversée : LAINÉG egagnal nu tse nohtyP el
Caractères : 31
Mots : 6
Voyelles : 10
Contraintes :
- La version nettoyée (
strip()) et la version minuscule sont calculées une fois chacune, stockées dans des variables, puis réutilisées. - Les crochets
[...]autour des deux premières lignes rendent les espaces visibles — c'est une vraie technique de débogage, garde-la. - Nombre de mots sans boucle ni split : compte les espaces de la version nettoyée et ajoute 1.
- Voyelles : additionne six
count()sur la version minuscule. - Étiquettes alignées avec
{'...':<12}, comptages alignés avec{...:>4}. - f-strings partout. Durée cible : 30–45 min.
Extension (facultative) : affiche aussi Espaces doubles : True/False avec " " in cleaned — et teste ton compteur de mots avec deux espaces entre deux mots. Tu verras sa limite ; note-la dans ton journal (⏩ split() la corrigera au niveau 3).
12. Correction / méthode de correction
Les solutions détaillées sont dans solutions/serie-01-3-solutions.md.
Méthode : (1) finis TOUS les exercices avant d'ouvrir les solutions ; (2) compare le raisonnement, pas seulement le code — une solution différente qui marche est souvent valable ; (3) pour chaque écart, écris une ligne dans ton journal : « je pensais X, en fait Y » ; (4) refais de tête l'exercice raté, le lendemain.
13. À retenir
- Les indices commencent à 0 ;
word[-1]= dernier caractère ; hors limites →IndexError. - Slicing
[start:stop:step]:stopexclu, bornes optionnelles,[::-1]retourne la chaîne — et un slice ne plante jamais, même faux. - Les chaînes sont immuables :
word[0] = "P"→TypeError; toute méthode retourne une nouvelle chaîne. - Donc : toujours récupérer le résultat —
s = s.strip(), jamaiss.strip()tout seul. - Méthodes essentielles :
upper,lower,strip,replace,startswith,count,find— et le duosplit/join(⏩ niveau 3). - Chercher :
inpar défaut ;findseulement pour la position — et il retourne -1 (pas une erreur) si absent. len()est une fonction (len(s)), pas une méthode (s.len()→AttributeError).- F-strings :
{x:>10},{x:<10},{x:^10}pour aligner ; combinables avec.2f({x:>8.2f}).
14. Questions de révision
- Que valent
word[0],word[-1]etword[len(word)]pourword = "python"? Lequel des trois pose problème, et pourquoi ? - Pourquoi
word[0:3]contient-il exactement 3 caractères ? Quel est l'avantage de cette convention ? word[2:50]plante-t-il ? Etword[50]? Explique la différence de comportement entre indexation et slicing.- Que signifie « les chaînes sont immuables » ? Donne le bug bruyant ET le bug silencieux que cette règle provoque.
- Pourquoi
message.strip()seul sur une ligne ne sert-il à rien ? - Que retourne
"abc".find("z")? Pourquoi est-ce plus dangereux qu'une erreur ? - Quelle est la différence entre
len(word)etword.count("a")— dans la syntaxe et dans ce qu'ils mesurent ? - Comment affiches-tu
19.9sous la forme19.90(aligné à droite sur 8, deux décimales) dans une f-string ?
15. Checklist de compréhension
- Je donne le caractère à n'importe quel indice (positif ou négatif) d'une chaîne courte, de tête.
- Je peux écrire de mémoire le slice qui extrait les 3 derniers caractères, et celui qui retourne la chaîne.
- Je peux expliquer à voix haute pourquoi
s.upper()sans assignation ne fait rien. - Je peux provoquer volontairement un
IndexErroret leTypeErrord'immutabilité, et les lire de bas en haut. - Je sais choisir entre
in,findetcountselon la question posée. - Je sais aligner du texte et des nombres en colonnes avec
:<,:>,:.2f. - J'ai fait le mini-projet et il tourne sans erreur, y compris avec des espaces autour de la saisie.
Si une case reste vide : refais les exercices de la section correspondante demain, AVANT d'ouvrir la leçon suivante.
16. Commit conseillé
git add lessons/level-01-bases/exercices/mes-reponses/
git commit -m "exercises: complete lesson 01-3 strings"
Puis, si c'est ta fin de session : entrée de journal + git push.