Aller au contenu
Topos Uranos

Résumé

Le texte est la donnée la plus fréquente d’un programme : des noms, des messages, des lignes d’un fichier, des ordres d’un utilisateur. Cette leçon le traite comme ce qu’il est pour le C++, une séquence de caractères dotée d’opérations propres. Elle présente std::string, que l’on crée, concatène, compare et parcourt comme un vecteur de char qui fait office de texte ; ses opérations de recherche et d’extraction (find, substr, starts_with, contains) et la valeur std::string::npos, par laquelle find dit qu’il n’a rien trouvé ; std::string_view, la vue qui observe un texte sans le copier, et la condition dont elle dépend : que le texte observé continue d’exister. La leçon se clôt sur les chaînes C, pour lire le code d’autrui, et sur l’ordre dans lequel le C++ compare et trie les mots.

Objectifs d’apprentissage

  1. Utiliser les opérations de std::string (size, +, find, substr, starts_with, contains) pour résoudre des problèmes de texte.
  2. Interpréter le résultat de find et le comparer à std::string::npos, et prévoir ce qui se passe si on le traite comme un nombre quelconque.
  3. Recevoir du texte dans une fonction sous la forme d’un std::string_view et expliquer sur un exemple pourquoi une vue ne doit pas survivre à la chaîne qu’elle observe.
  4. Lire du code avec des chaînes C et expliquer leurs différences avec std::string : le zéro terminal, la taille fixe et la somme de deux littéraux, qui ne compile pas.

La chaîne std::string

Créer, concaténer, parcourir

La leçon Caractères, booléens, conversions et constantes a présenté le caractère, char, comme un petit nombre qu’une table convertit en lettre. Un texte est une suite de caractères ; s’il en est ainsi, il peut être rangé dans un std::vector<char>, et ce que la leçon Vecteurs a enseigné des séquences vaudrait pour lui. Or on demande au texte des opérations qu’on ne demande pas à une séquence quelconque : lui accoler un autre texte, y chercher un mot, en extraire un morceau, l’écrire d’un seul coup. Il s’ensuit un type propre, la chaîne de caractères, std::string, de l’en-tête <string> : un vecteur de char qui fait office de texte.

Programme en C++
#include <format>#include <print>#include <string>​int main(){    const std::string first{"Ada"};    std::string greeting{"Bonjour, " + first};    greeting += '.';    std::println("{} ({} caractères)", greeting, greeting.size());    std::println("Premier « {} », dernier « {} »", greeting[0], greeting.back());    int spaces{0};    for (const char c : greeting) {        if (c == ' ') {            ++spaces;        }    }    std::println("Espaces : {}", spaces);    const std::string code{std::format("{}-{:03}", "MAT", 7)};    std::println("{} {}", code, code == "MAT-007");}
Sortie
Bonjour, Ada. (13 caractères)Premier « B », dernier « . »Espaces : 1MAT-007 true

La ligne 7 crée une chaîne à partir d’un littéral. La ligne 8 la concatène : l’opérateur + entre une chaîne et un littéral produit une nouvelle chaîne avec les caractères des deux, l’un à la suite de l’autre. La ligne 9 ajoute un caractère à la fin avec +=, qui admet aussi une chaîne ou un littéral. Le reste est ce que l’on savait déjà des vecteurs : size() compte les caractères (13, car la virgule, l’espace et le point en sont aussi), greeting[0] est le premier et back() le dernier, et la boucle des lignes 13 à 17 parcourt la chaîne caractère par caractère avec la forme d’intervalle de la leçon Boucles. Comme le vecteur, une chaîne admet en outre push_back, pop_back et empty, et la bibliothèque de GCC 15, lorsqu’on n’optimise pas, vérifie ses indices avec operator[] (avec optimisation, cette vérification disparaît).

Les deux dernières lignes tiennent ce qu’avait annoncé la leçon Concevoir avec des fonctions. std::format, de <format>, reçoit les mêmes marqueurs que std::println, avec les mêmes spécifications de format ({:03} complète avec des zéros jusqu’à trois chiffres), mais au lieu d’écrire le résultat il le renvoie sous forme de chaîne : c’est la manière de construire un texte que l’on range ensuite, que l’on compare ou que l’on passe à une fonction. Et == compare deux chaînes par leur contenu : elles sont égales si elles ont la même taille et les mêmes caractères dans le même ordre. Par conséquent, code == "MAT-007" vaut true, bien que le texte de gauche ait été construit par morceaux et que celui de droite soit un littéral.

Comparer

Outre == et !=, les chaînes admettent <, <=, > et >=, qui les comparent selon l’ordre lexicographique, celui du dictionnaire : on parcourt les deux en parallèle, et à la première position où elles diffèrent, c’est le caractère le plus petit qui décide ; si l’une s’épuise avant sans aucune différence, la plus courte est la plus petite. Ainsi, la chaîne « date » est plus petite que « dote », parce que 'a' < 'o' en deuxième position, et « mer » est plus petite que « merle », parce que la première est un préfixe de la seconde. Les opérateurs comparent des chaînes : entre deux littéraux écrits entre guillemets, ils comparent autre chose, car un littéral n’est pas un std::string mais un tableau, comme on le verra dans la section sur les chaînes C. Quel caractère est « plus petit », c’est son code qui le décide, non l’alphabet ; la dernière section de la leçon montre ce qui en découle.