Section 9.2

Chercher dans un texte

Nettoyer, interroger, situer — et la convention du −1, qui n'est pas le zéro des absences.

Une ligne lue quelque part ne se présente jamais comme on l’espérait : des espaces au bord, une information qu’il faut situer avant de la prendre. Quatre méthodes suffisent à la mettre en forme, et elles viennent du même StrText que la 9.1 a importé.

import { StrText } from "std/extensions/str-text.arc";

extend archetype str StrText;

@entry(demarrer)
object MonProgramme {
    demarrer() {
        var ligne := "  mandragore : 3 doses  ";
        var propre := ligne.trimmed();

        var connu := propre.startsWith(prefix: "mandragore");
        var rang := propre.indexOf(needle: ":", since: 0);

        Console.print(text: $"nettoyée : « {propre} »");
        Console.print(text: $"commence par « mandragore » : {connu}");
        Console.print(text: $"contient « dose » : {propre.contains(needle: "dose")}");
        Console.print(text: $"les deux points sont au rang {rang}");
        Console.print(text: $"l'ingrédient : {propre.substring(start: 0, end: rang).trimmed()}");
        Console.print(text: $"« belladone » : {propre.indexOf(needle: "belladone", since: 0)}");
    }
}

trimmed rend une copie, il ne modifie rien

var propre := ligne.trimmed();

ligne n’a pas bougé : elle vaut toujours " mandragore : 3 doses ", espaces compris. trimmed rend un texte, il n’en corrige pas un. C’est vrai de toutes les méthodes de cette section, et c’est vrai de str en général : un texte, une fois écrit, ne se retouche pas — on en fabrique un autre. D’où l’affectation : sans elle, le travail est fait puis jeté.

Répondre par oui ou par non

propre.startsWith(prefix: "mandragore")
propre.contains(needle: "dose")

Deux bool, qui se posent tels quels dans un if ou se combinent avec le and et le or de la 3.3. endsWith existe aussi, symétrique de startsWith.

Situer, et la convention du −1

propre.indexOf(needle: ":", since: 0)

indexOf rend le rang de la première occurrence à partir de since — ici 11, et l’on peut chercher la suivante en repartant de 12. Quand l’aiguille n’est nulle part, la réponse est -1.

Pourquoi -1, alors que le cours a présenté le zéro comme la marque de l’absence en 5.2 ? Parce qu’ici zéro est déjà pris : c’est le rang du tout premier caractère, une réponse parfaitement valable. Une absence ne peut pas se marquer par une valeur qui veut dire déjà autre chose, et -1 — un rang qui n’existe pas — est libre. Retenez donc la forme :

if (propre.indexOf(needle: "belladone", since: 0) >= 0) {

et non une comparaison à zéro, qui manquerait le premier caractère.

C’est d’ailleurs tout ce que contains est : indexOf(…) >= 0, écrit une fois pour n’avoir plus à y penser.

Situer, puis couper

Les deux dernières lignes du programme font ensemble ce à quoi tout cela sert :

propre.substring(start: 0, end: rang).trimmed()

indexOf a donné le rang des deux points, substring prend ce qui précède, trimmed retire l’espace resté au bord — et il reste mandragore. Les méthodes s’enchaînent parce que chacune rend un texte, et qu’un texte sait faire tout ce que StrText lui a appris.

À vous

  • Affichez ligne.length puis propre.length : la première n’a rien perdu.
  • Cherchez ":" à partir de 12 plutôt que de 0 : il n’y a pas de second deux-points, donc -1.
  • Remplacez >= 0 par > 0 dans le if ci-dessus, et cherchez "mandragore" : la condition est fausse alors que le mot est bien là, au rang zéro. C’est exactement le piège que la convention du -1 demande d’éviter.
  • Prenez ce qui suit les deux points — substring(start: rang + 1, end: propre.length) — et nettoyez-le. Ce morceau-là, 3 doses, est le sujet de la sous-section suivante.