Le C++ étant un langage basé sur le C, il souffre des mêmes limitations concernant les types de données avancés que celui-ci. Pour pallier cet inconvénient, la bibliothèque standard C++ définit des types complémentaires sous forme de classes C++, éventuellement template, et permettant de satisfaire aux besoins les plus courants. Parmi ces types, on notera avant tout le type basic_string, qui permet de manipuler les chaînes de caractères de manière plus simple et plus sûre qu'avec des pointeurs et des tableaux de caractères. Mais la bibliothèque standard définit également des classes utilitaires qui permettent de manipuler les autres types plus facilement, ainsi que des types capables d'utiliser toutes les ressources de la machine pour les calculs numériques avancés.
La classe template basic_string de la bibliothèque standard, déclarée dans l'en-tête string, facilite le travail du programmeur et permet d'écrire du code manipulant des textes de manière beaucoup plus sûre. En effet, cette classe encapsule les chaînes de caractères C classiques et fournissent des services extrêmement intéressants qui n'étaient pas disponibles auparavant. En particulier, la classe basic_string dispose des caractéristiques suivantes :
compatibilité quasi-totale avec les chaînes de caractères C standards ;
gestion des chaînes à taille variable ;
prise en charge de l'allocation dynamique de la mémoire et de sa libération en fonction des besoins et de la taille des chaînes manipulées ;
définition des opérateurs de concaténation, de comparaison et des principales méthodes de recherche dans les chaînes de caractères ;
intégration totale dans la bibliothèque standard, en particulier au niveau des flux d'entrée / sortie.
Comme il l'a été dit plus haut, la classe basic_string est une classe template. Cela signifie qu'elle est capable de prendre en charge des chaînes de n'importe quel type de caractère. Pour cela, elle ne se base que sur la classe des traits du type de caractère manipulé. Il est donc parfaitement possible de l'utiliser avec des types définis par l'utilisateur, pourvu que la classe des traits des caractères soit définie pour ces types. Bien entendu, la classe basic_string peut être utilisée avec les types de caractères du langage, à savoir char et wchar_t.
Les déclarations de l'en-tête string sont essentiellement les suivantes :
template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { public: // Types typedef traits traits_type; typedef typename traits::char_type value_type; typedef Allocator allocator_type; typedef typename Allocator::size_type size_type; typedef typename Allocator::difference_type difference_type; typedef typename Allocator::reference reference_type; typedef typename Allocator::const_reference const_reference; typedef typename Allocator::pointer pointer; typedef typename Allocator::const_pointer const_pointer; // Constante utilisée en interne et représentant la valeur maximale // du type size_type : static const size_type npos = static_cast<size_type>(-1); // Constructeurs et destructeur : explicit basic_string(const Allocator &allocateur = Allocator()); basic_string(const basic_string &source, size_type debut = 0, size_type longueur = npos, const Allocator &allocateur = Allocator()); basic_string(const charT *chaine, size_type nombre, const Allocator &allocateur = Allocator()); basic_string(const charT *chaine, const Allocator &allocateur = Allocator()); basic_string(size_type nombre, charT caractere, const Allocator &allocateur = Allocator()); template <class InputIterator> basic_string(InputIterator debut, InputIterator fin, const Allocator &allocateur = Allocator()); ~basic_string(); // Itérateurs : typedef type_privé iterator; typedef type_privé const iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; // Accesseurs : size_type size() const; size_type length() const; size_type max_size() const; size_type capacity() const; bool empty() const; allocator_type get_allocator() const; // Manipulateurs : void resize(size_type taille, charT caractere); void resize(size_type taille); void reserve(size_type taille = 0); // Accès aux données de la chaîne : const_reference operator[](size_type index) const; reference operator[](size_type index); const_reference at(size_type index) const; reference at(size_type index); const charT *c_str() const; const charT *data() const; size_type copy(charT *destination, size_type taille, size_type debut = 0) const; basic_string substr(size_type debut = 0, size_type taille = npos) const; // Affectation : basic_string &operator=(const basic_string &source); basic_string &operator=(const charT *source); basic_string &operator=(charT caractere); basic_string &assign(const basic_string &source); basic_string &assign(const basic_string &source, size_type position, size_type nombre); basic_string &assign(const charT *chaine, size_type nombre); basic_string &assign(const charT *chaine); basic_string &assign(size_type nombre, charT caractere); template <class InputIterator> basic_string &assign(InputIterator debut, InputIterator fin); // Concaténation et ajout : basic_string &operator+=(const basic_string &source); basic_string &operator+=(const charT *chaine); basic_string &operator+=(charT caractere); basic_string &append(const basic_string &source); basic_string &append(const basic_string &source, size_type position, size_type nombre); basic_string &append(const charT *chaine, size_type nombre); basic_string &append(const charT *chaine); basic_string &append(size_type nombre, charT caractere); template <class InputIterator> basic_string &append(InputIterator debut, InputIterator fin); // Insertion et extraction : basic_string &insert(size_type position, const basic_string &source); basic_string &insert(size_type position, const basic_string &source, size_type debut, size_type nombre); basic_string &insert(size_type position, const charT *chaine, size_type nombre); basic_string &insert(size_type position, const charT *chaine); basic_string &insert(size_type position, size_type nombre, charT caractere); iterator insert(iterator position, charT caractere = charT()); void insert(iterator position, size_type nombre, charT caractere); template <class InputIterator> void insert(iterator position, InputIterator debut, InputIterator fin); // Suppression : basic_string &erase(size_type debut = 0, size_type longueur = npos); iterator erase(iterator position); iterator erase(iterator debut, iterator fin); void clear(); // Remplacement et échange : basic_string &replace(size_type position, size_type longueur, const basic_string &remplacement); basic_string &replace(size_type position, size_type longueur, const basic_string &remplacement, size_type debut, size_type taille); basic_string &replace(size_type position, size_type longueur, const charT *remplacement, size_type taille); basic_string &replace(size_type position, size_type longueur, const charT *remplacement); basic_string &replace(size_type position, size_type longueur, size_type nombre, charT caractere); basic_string &replace(iterator debut, iterator fin, const basic_string &remplacement); basic_string &replace(iterator debut, iterator fin, const charT *remplacement, size_type taille); basic_string &replace(iterator debut, iterator fin, const charT *remplacement); basic_string &replace(iterator debut, iterator fin, size_type nombre, charT caractere); template <class InputIterator> basic_string &replace(iterator debut, iterator fin, InputIterator debut_remplacement, InputIterator fin_remplacement); void swap(basic_string<charT, traits, Allocator> &chaine); // Comparaison : int compare(const basic_string &chaine) const; int compare(size_type debut1, size_type longueur1, const basic_string &chaine, size_type debut2, size_type longueur2) const; int compare(const charT *chaine) const; int compare(size_type debut, size_type longueur, const charT *chaine, size_type taille = npos) const; // Recherche : size_type find(const basic_string &motif, size_type position = 0) const; size_type find(const charT *motif, size_type position, size_type taille) const; size_type find(const charT *motif, size_type position = 0) const; size_type find(charT caractere, size_type position = 0) const; size_type rfind(const basic_string &motif, size_type position = npos) const; size_type rfind(const charT *motif, size_type position, size_type taille) const; size_type rfind(const charT *motif, size_type position = npos) const; size_type rfind(charT caractere, size_type position = npos) const; size_type find_first_of(const basic_string &motif, size_type position = 0) const; size_type find_first_of(const charT *motif, size_type position, size_type taille) const; size_type find_first_of(const charT *motif, size_type position = 0) const; size_type find_first_of(charT caractere, size_type position = 0) const; size_type find_last_of(const basic_string &motif, size_type position = npos) const; size_type find_last_of(const charT *motif, size_type position, size_type taille) const; size_type find_last_of(const charT *motif, size_type position = npos) const; size_type find_last_of(charT caractere, size_type position = npos) const; size_type find_first_not_of(const basic_string &motif, size_type position = 0) const; size_type find_first_not_of(const charT *motif, size_type position, size_type taille) const; size_type find_first_not_of(const charT *motif, size_type position = 0) const; size_type find_first_not_of(charT caractere, size_type position = 0) const; size_type find_last_not_of(const basic_string &motif, size_type position = npos) const; size_type find_last_not_of(const charT *motif, size_type position, size_type taille) const; size_type find_last_not_of(const charT *motif, size_type position = npos) const; size_type find_last_not_of(charT caractere, size_type position = npos) const; }; typedef basic_string<char> string; typedef basic_string<wchar_t> wstring;Les opérateurs de concaténation, de comparaison et de sérialisation dans les flux d'entrée / sortie sont également définis dans cet en-tête et n'ont pas été reportés ici par souci de clarté. Comme vous pouvez le voir, la classe basic_string dispose d'un grand nombre de méthodes. Nous allons à présent les détailler dans les paragraphes suivants.
La bibliothèque standard définit deux types chaînes de caractères pour les types standards de caractères du langage : le type string pour les char, et le type wstring pour les wchar_t. En pratique, ce seront donc ces types qui seront utilisés dans les programmes. Les exemples de la suite de ce document utiliseront donc le type string, mais vous êtes libre d'utiliser des instances de la classe basic_string pour d'autres types de caractères.
La manière la plus simple de construire une basic_string est simplement de la déclarer, sans paramètres. Cela a pour conséquence d'appeler le constructeur par défaut, et d'initialiser la chaîne à la chaîne vide.
En revanche, si vous désirez initialiser cette chaîne, plusieurs possibilités s'offrent à vous. Outre le constructeur de copie, qui permet de copier une autre basic_string, il existe plusieurs surcharges du constructeur permettant d'initialiser la chaîne de différentes manières. Le constructeur le plus utilisé sera sans aucun doute le constructeur qui prend en paramètre une chaîne de caractères C classique :
Il existe cependant une variante de ce constructeur, qui prend en paramètre le nombre de caractères de la chaîne source à utiliser pour l'initialisation de la basic_string. Ce constructeur devra être utilisé dans le cas des tableaux de caractères, qui contiennent des chaînes de caractères qui ne sont pas nécessairement terminées par un caractère nul : La ligne précédente initialise la chaîne chaine avec la chaîne de caractères "Valeur", car seuls les six premiers caractères de la chaîne d'initialisation sont utilisés.Il est également possible d'initialiser une basic_string avec une partie de chaîne de caractères seulement. Pour cela, il faut utiliser le constructeur template, qui prend en paramètre un itérateur référençant le premier caractère à utiliser lors de l'initialisation de la basic_string et un itérateur référençant le caractère suivant le dernier caractère à utiliser. Bien entendu, ces deux itérateurs sont de simples pointeurs de caractères si les caractères devant servir à l'initialisation sont dans une chaîne de caractères C ou dans un tableau de caractères. Cependant, ce peut être des itérateurs d'un conteneur quelconque, pourvu que celui-ci contienne bien une séquence de caractères et que le deuxième itérateur se trouve bien après le premier dans cette séquence. Notez que le deuxième itérateur ne référence pas le dernier caractère de la séquence d'initialisation, mais bien le caractère suivant. Il peut donc valoir la valeur de fin de l'itérateur du conteneur source. Ainsi, le code suivant :
a strictement le même effet que celui de l'exemple précédent.Enfin, il existe un constructeur dont le but est d'initialiser une basic_string avec une suite de caractères identiques d'une certaine longueur. Ce constructeur n'est réellement pas difficile à utiliser, puisqu'il suffit de lui fournir en paramètre le nombre de caractères que la basic_string devra contenir et la valeur du caractère qui devra être répété dans cette chaîne.
Vous remarquerez que tous ces constructeurs prennent en dernier paramètre une instance d'allocateur mémoire à utiliser pour les opérations d'allocation et de libération de la mémoire que la chaîne est susceptible d'avoir à faire. Vous pouvez spécifier une instance quelconque, ou utiliser la valeur par défaut fournie par les déclarations des constructeurs. Cette valeur par défaut est tout simplement une instance temporaire de l'allocateur spécifié en paramètre template de la classe basic_string. Par défaut, cet allocateur est l'allocateur standard, pour lequel toutes les instances permettent d'accéder à la même mémoire. Il n'est donc pas nécessaire de spécifier l'instance à utiliser, puisqu'elles sont toutes fonctionnellement identiques. En pratique donc, il est très rare d'avoir à spécifier un allocateur mémoire dans ces constructeurs.
La classe basic_string fournit un certain nombre d'accesseurs
permettant d'obtenir des informations sur son état et sur la chaîne de caractères qu'elle contient.
L'une des informations les plus intéressantes est sans nul doute la longueur de cette chaîne. Elle peut
être obtenue à l'aide de deux accesseurs, qui sont strictement équivalents : size
et length
. Vous pouvez utiliser l'un ou l'autre, selon votre convenance. Par ailleurs,
si vous désirez simplement savoir si la basic_string est vide, vous pouvez appeler la méthode
empty
.
Note : Attention, contrairement à ce que son nom pourrait laisser penser, la méthode
empty
ne vide pas la basic_string !
La taille maximale qu'une basic_string peut contenir est
souvent directement liée à la quantité de mémoire disponible, puisque la chaîne de caractères qu'elle
contient est allouée dynamiquement. Il n'y a donc souvent pas beaucoup d'intérêt à obtenir cette taille,
mais vous pouvez malgré tout le faire, grâce à la méthode max_size
.
La quantité de mémoire réellement allouée par une basic_string
peut être supérieure à la longueur de la chaîne de caractères contenue. En effet, la classe
basic_string peut conserver une marge de manoeuvre, pour le cas où la chaîne devrait être
agrandie à la suite d'une opération particulière. Cela permet de réduire les réallocations de mémoire,
qui peuvent être très coûteuses lorsque la mémoire se fragmente (la chaîne doit être recopiée vers
un nouvel emplacement si un autre bloc mémoire se trouve juste après le bloc mémoire à réallouer).
Cette quantité de mémoire peut être obtenue à l'aide de la méthode capacity
.
Nous verrons comment réserver de la place mémoire en prévision d'un redimensionnement ultérieur
dans la section suivante.
Dans le cas où vous utiliseriez un allocateur différent de l'allocateur
par défaut, vous pouvez obtenir une copie de cet allocateur grâce à la méthode
get_allocator
. Il est relativement rare d'avoir à utiliser cette méthode.
Une fois qu'une basic_string a été construite, il est possible de la modifier a posteriori pour la réduire, l'agrandir ou augmenter sa capacité. Ces opérations peuvent être réalisées à l'aide de méthodes fournies à cet effet.
La méthode resize
permet de modifier la taille
de la chaîne de caractères stockée dans la basic_string. Dans sa version la plus simple,
elle prend en paramètre la nouvelle taille que la chaîne doit avoir. Si cette taille est inférieure
à la taille courante, la chaîne est tronquée. En revanche, si cette taille est supérieure à la taille
actuelle, la chaîne est étendue et les nouveaux caractères sont initialisés avec la valeur des caractères
définie par le constructeur par défaut de leur classe. Pour les types prédéfinis char et
wchar_t, cette valeur est toujours le caractère nul. Les données stockées dans
la basic_string représentent donc toujours la même chaîne de caractères C, puisque
ces chaînes utilisent le caractère nul comme marqueur de fin de chaîne. Ainsi, la longueur renvoyée
par la méthode size
peut être différente de la longueur de la chaîne C contenue
par la basic_string.
Exemple 14-1. Redimensionnement d'une chaîne
#include <iostream> #include <string> #include <string.h> using namespace std; int main(void) { string s("123"); s.resize(10); cout << s << endl; // La nouvelle taille vaut bien 10 : cout << "Nouvelle taille : " << s.length() << endl; // mais la longueur de la chaîne C reste 3 : cout << "Longueur C : " << strlen(s.c_str()) << endl; return 0; }
Note : La méthode
c_str
utilisée dans cet exemple sera décrite en détail dans la section suivante. Elle permet d'obtenir l'adresse de la chaîne C stockée en interne dans la basic_string.La fonction
strlen
quant à elle est une des fonctions de manipulation des chaînes de caractères de la bibliothèque C. Elle est définie dans le fichier d'en-tête string.h (que l'on ne confondra pas avec l'en-tête string de la bibliothèque standard C++), et renvoie la longueur de la chaîne de caractères qui lui est fournie en paramètre.
Si la valeur par défaut utilisée pour les caractères complémentaires
dans la méthode resize
n'est pas celle qui est désirée, il faut en utiliser
une autre version. Cette deuxième version prend, en plus de la nouvelle taille de la chaîne de caractères,
le caractère de remplissage à utiliser pour le cas où la nouvelle taille serait supérieure à la taille
initiale de la chaîne :
Nous avons vu précédemment que les basic_string étaient
susceptibles d'allouer plus de mémoire que nécessaire pour stocker leurs données afin de limiter
le nombre de réallocation mémoire. Ce mécanisme est complètement pris en charge par la bibliothèque,
et le programmeur n'a en général pas à s'en soucier. Cependant, il peut exister des situations où
l'on sait à l'avance la taille minimale qu'une chaîne doit avoir pour permettre de travailler dessus
sans craindre de réallocations mémoire successives. Dans ce cas, on a tout intérêt à fixer la capacité
de la chaîne directement à cette valeur, afin d'optimiser les traitements. Cela est réalisable à l'aide
de la méthode reserve
. Cette méthode prend en paramètre la capacité minimale que
la basic_string doit avoir. La nouvelle capacité n'est pas forcément égale à ce paramètre
après cet appel, car la basic_string peut allouer plus de mémoire que demandé.
Exemple 14-2. Réservation de mémoire dans une chaîne
#include <iostream> #include <string> using namespace std; int main(void) { string s; cout << s.capacity() << endl; s.reserve(15); s = "123"; cout << s.capacity() << endl; return 0; }
Note : Les méthodes
resize
etreserve
peuvent effectuer une réallocation de la zone mémoire contenant la chaîne de caractères. Par conséquent, toutes les références sur les caractères de la chaîne et tous les itérateurs sur cette chaîne deviennent invalide à la suite de leur exécution.
Les caractères des basic_string peuvent être accédés
de nombreuses manières. Premièrement, la classe basic_string surcharge l'opérateur d'accès
aux éléments d'un tableau, et l'on pourra les utiliser pour obtenir une référence à un des caractères
de la chaîne à partir de son indice. Cet opérateur n'est défini que pour les indices valides dans la chaîne
de caractères, à savoir les indices variant de 0 à la valeur retournée par la méthode
size
de la chaîne moins un :
#include <iostream> #include <string> using namespace std; int main(void) { string s("azc"); // Remplace le deuxième caractère de la chaîne par un 'b' : s[1] = 'b'; cout << s << endl; return 0; }
Lorsqu'il est appliqué à une basic_string constante, l'opérateur tableau peut renvoyer la valeur du caractère dont l'indice est exactement la taille de la chaîne. Il s'agit évidemment du caractère nul servant de marqueur de fin de chaîne. En revanche, la référence renvoyée par cet opérateur pour toutes les autres valeurs, ainsi que par l'opérateur tableau appliqué aux chaînes non constante pour le caractère de fin de chaîne ne sont pas valides. Le comportement des programmes qui effectuent de tels accès est imprévisible.
Il existe une autre possibilité pour accéder aux caractères d'une
basic_string. Il s'agit de la méthode at
. Contrairement à l'opérateur
tableau, cette méthode permet d'effectuer un contrôle sur la validité de l'indice utilisé. Elle
renvoie, comme l'opérateur de tableau de la classe basic_string, la référence du caractère
dont l'indice est spécifié en paramètre. Cependant, elle effectue au préalable un contrôle sur la validité
de cet indice, qui doit toujours être strictement inférieur à la taille de la chaîne. Dans le cas
contraire, la méthode at
lance une exception out_of_range :
#include <iostream> #include <string> #include <stdexcept> using namespace std; int main(void) { string s("01234"); try { s.at(5); } catch (const out_of_range &) { cout << "Débordement !" << endl; } return 0; }
La classe basic_string ne contient pas d'opérateur de transtypage vers les types des chaînes de caractères C classique, à savoir le type pointeur sur caractère et pointeur sur caractère constant. C'est un choix de conception, qui permet d'éviter les conversions implicites des basic_string en chaîne C classique, qui pourraient être extrêmement dangereuses. En effet, ces conversions conduiraient à obtenir implicitement des pointeurs qui ne seraient plus valides dès qu'une opération serait effectuée sur la basic_string source. Cependant, la classe basic_string fournit deux méthodes permettant d'obtenir de tels pointeurs de manière explicite. Le programmeur prend donc ses responsabilités lorsqu'il utilise ces méthodes, et est supposé savoir dans quel contexte ces pointeurs sont valides.
Ces deux méthodes sont respectivement la méthode data
et la méthode c_str
. La première méthode renvoie un pointeur sur les données de
la basic_string. Ces données ne sont rien d'autre qu'un tableau de caractères, dont
la dimension est exactement la valeur retournée par la méthode size
ou par la méthode
length
. Ce tableau peut contenir des caractères de terminaison de chaîne, si par
exemple une telle valeur a été écrite explicitement ou a été introduite suite à un redimensionnement
de la chaîne. La méthode c_str
en revanche retourne un pointeur sur la chaîne
de caractères C contenue dans la basic_string. Contrairement aux données renvoyées par
la méthode data
, cette chaîne est nécessairement terminée par un caractère de fin
de chaîne. Cette méthode sera donc utilisée partout où l'on veut obtenir une chaîne de caractères C
classique, mais elle ne devra pas être utilisée pour accéder aux données situées après ce caractère de
fin de chaîne.
Exemple 14-3. Accès direct aux données d'une chaîne
#include <iostream> #include <string> using namespace std; int main(void) { string s("123456"); // La chaîne C est coupée au troisième caractère : s[3] = 0; cout << s.c_str() << endl; // Mais on peut quand même obtenir les caractères suivants : cout << s.data()[4] << s.data()[5] << endl; return 0; }
Notez que ces méthodes renvoient des pointeurs sur des données constantes. Cela est normal, car il est absolument interdit de modifier les données internes à la basic_string qui a fourni ces pointeurs. Vous ne devez donc en aucun cas essayer d'écrire dans ces tableaux de caractères, même en faisant un transtypage au préalable.
Enfin, la classe basic_string définit des itérateurs
à accès aléatoires permettant d'accéder à ses données comme s'il s'agissait d'une chaîne de caractères
standard. Les méthodes begin
et end
permettent d'obtenir
respectivement un itérateur sur le premier caractère de la chaîne et la valeur de fin de ce même itérateur.
De même, les méthodes rbegin
et rend
permettent de parcourir
les données de la basic_string en sens inverse. Prenez garde au fait que ces itérateurs
ne sont valides que tant qu'aucune méthode modifiant la chaîne n'est appelée.
La classe basic_string fournit tout un ensemble de méthodes permettant d'effectuer les opérations les plus courantes sur les chaînes de caractères. C'est cet ensemble de méthodes qui font tout l'intérêt de cette classe par rapport aux chaînes de caractères classiques du langage C, car elles prennent en charge automatiquement les allocations mémoire et les copies de chaînes que l'implémentation de ces fonctionnalités peut imposer. Ces opérations comprennent l'affectation et la concaténation de deux chaînes de caractères, l'extraction d'une sous-chaîne, ainsi que la suppression et le remplacement de caractères dans une chaîne.
Plusieurs surcharges de l'opérateur d'affectation sont définies dans la classe basic_string. Ces surcharges permettent d'affecter une nouvelle valeur à une chaîne, en remplaçant éventuellement l'ancienne valeur. Dans le cas d'un remplacement, la mémoire consommée par l'ancienne valeur est automatiquement réutilisée ou libérée si une réallocation est nécessaire. Ces opérateurs d'affectation peuvent prendre en paramètre une autre basic_string, une chaîne de caractères C classique ou même un simple caractère. Leur utilisation est donc directe, il suffit simplement d'écrire une affectation normale.
Il est impossible, avec l'opérateur d'affectation, de fournir
des paramètres supplémentaires comme ceux dont les constructeurs de la classe basic_string
disposent par exemple. C'est pour cette raison qu'une autre méthode, la méthode assign
,
a été définie pour permettre de faire des affectations plus complexes.
Les premières versions de ces méthodes permettent bien entendu d'effectuer l'affectation d'une autre basic_string ou d'une chaîne de caractères C classique. Cependant, il est également possible de spécifier la longueur de la portion de chaîne à copier en deuxième paramètre pour les chaînes C, et la position ainsi que le nombre de caractères à copier dans le cas d'une basic_string. Il est également possible de réinitialiser une basic_string avec un caractère spécifique, en donnant et le nombre de caractères à dupliquer dans la chaîne en premier paramètre et la valeur de ce caractère en deuxième paramètre. Enfin, il existe une version template de cette méthode permettant d'affecter à la basic_string la séquence de caractères compris entre deux itérateurs d'un conteneur.
Exemple 14-4. Affectation de chaîne de caractères
#include <iostream> #include <string> using namespace std; int main(void) { string s1, s2; char *p="1234567890"; // Affecte "345" à s1 : s1.assign(p+2, p+6); cout << s1 << endl; // Affecte les deux premiers caractères de s1 à s2 : s2.assign(s1, 0, 2); cout << s2 << endl; // Réinitialise s1 avec des 'A' : s1.assign(5, 'A'); cout << s1 << endl; return 0; }
De manière similaire, l'opérateur d'affectation avec addition
'+=' a été surchargé afin de permettre les concaténations de chaînes de caractères
de manière intuitive. Cet opérateur permet d'ajouter aussi bien une autre basic_string
qu'une chaîne de caractères C classique ou un unique caractère à la fin d'une basic_string.
Comme cet opérateur est trop restrictif lorsqu'il s'agit de concaténer une partie seulement d'une autre
chaîne à une basic_string, un jeu de méthodes append
a été défini.
Ces méthodes permettent d'ajouter à une basic_string une autre basic_string
ou une chaîne de caractères C bien entendu, mais aussi d'ajouter une partie seulement de ces chaînes
ou un nombre déterminé de caractères. Toutes ces méthodes prennent les mêmes paramètres que les méthodes
assign
correspondantes et leur emploi ne devrait pas poser de problème particulier.
Exemple 14-5. Concaténation de chaînes de carctères
#include <iostream> #include <string> using namespace std; int main(void) { string s1 = "abcef"; string s2 = "ghijkl"; // Utilisation de l'opérateur de concaténation : s1+=s2; cout << s1 << endl; // Utilisation de la méthode append : s1.append("mnopq123456", 5); cout << s1 << endl; return 0; }
Nous avons vu que les méthodes data
et c_str
permettaient d'obtenir des pointeurs sur les données des
basic_string. Cependant, il est interdit de modifier les données de la basic_string
au travers de ces pointeurs. Or, il peut être utile, dans certaines situations, d'avoir à travailler
sur ces données, il faut donc pouvoir en faire une copie temporaire. C'est ce que permet la méthode
copy
. Cette fonction prend en paramètre un pointeur sur une zone de mémoire
devant accueillir la copie des données de la basic_string, le nombre de caractères
à copier, ainsi que le numéro du caractère de la basic_string à partir duquel la copie
doit commencer. Ce dernier paramètre est facultatif, car il prend par défaut la valeur 0 (la copie
se fait donc à partir du premier caractère de la basic_string.
Exemple 14-6. Copie de travail des données d'une basic_string
#include <iostream> #include <string> using namespace std; int main(void) { string s="1234567890"; // Copie la chaîne de caractères dans une zone de travail : char buffer[16]; s.copy(buffer, s.size(), 0); buffer[s.size()] = 0; cout << buffer << endl; return 0; }
La basic_string doit contenir suffisamment de
caractères pour que la copie puisse se faire. Si ce n'est pas le cas, elle lancera une exception
out_of_range. En revanche, la méthode copy
ne peut faire aucune
vérification quant à la place disponible dans la zone mémoire qui lui est passée en paramètre. Il
est donc de la responsabilité du programmeur de s'assurer que cette zone est suffisamment grande
pour accueillir tous les caractères qu'il demande.
La méthode copy
permet d'obtenir la copie
d'une sous-chaîne de la chaîne contenue dans la basic_string. Toutefois, si l'on
veut stocker cette sous-chaîne dans une autre basic_string, il ne faut pas utiliser cette
méthode. La méthode substr
permet en effet d'effectuer ce travail directement.
Cette méthode prend en premier paramètre le numéro du premier caractère à partir de la sous-chaîne
à copier, ainsi que sa longueur. Comme pour la méthode copy
, il faut que la
basic_string source contienne suffisamment de caractères faute de quoi une exception
out_of_range sera lancée.
La classe basic_string dispose de tout un jeu
de méthodes d'insertion de caractères ou de chaînes de caractères au sein d'une basic_string
existante. Toutes ces méthodes sont des surcharges de la méthode insert
. Ces surcharges
prennent toutes un paramètre en première position qui indique l'endroit où l'insertion doit être faite.
Ce paramètre peut être soit un numéro de caractère, indiqué par une valeur de type size_type,
soit un itérateur de la basic_string dans laquelle l'insertion doit être faite. Les autres
paramètres permettent de spécifier ce qui doit être inséré dans cette chaîne.
Les versions les plus simples de la méthode
insert
prennent en deuxième paramètre une autre basic_string ou
une chaîne de caractères C classique. Leur contenu sera inséré à l'emplacement indiqué. Lorsque
le deuxième paramètre est une basic_string, il est possible d'indiquer le numéro du premier
caractère ainsi que le nombre de caractères total à insérer. De même, lors de l'insertion d'une chaîne C
classique, il est possible d'indiquer le nombre de caractères de cette chaîne qui doivent être insérés.
Il existe aussi des méthodes insert
permettant d'insérer un ou plusieurs caractères à un emplacement donné dans la chaîne de caractères.
Ce caractère doit alors spécifié en deuxième paramètre, sauf si l'on veut insérer plusieurs caractères
identiques dans la chaîne, auquel cas on doit indiquer le nombre de caractères à insérer et la valeur
de ce caractère.
Enfin, il existe une version de la méthode
insert
qui prend en paramètre, en plus de l'itérateur spécifiant la position
à partir de laquelle l'insertion doit être faite dans la basic_string, deux autres
itérateurs d'un quelconque conteneur contenant des caractères. Utilisé avec des pointeurs de caractères,
cet itérateur peut être utilisé pour insérer un morceau quelconque de chaîne de caractères C dans
une basic_string.
Toutes ces méthodes renvoient généralement
la basic_string sur laquelle ils travaillent, sauf les méthodes qui prennent en paramètre
un itérateur. Ces méthodes supposent en effet que la manipulation de la chaîne de caractères se fait
par l'intermédiaire de cet itérateur, et non par l'intermédiaire d'une référence sur
la basic_string. Cependant, la méthode insert
permettant d'insérer
un caractère unique à un emplacement spécifié par un itérateur renvoie la valeur de l'itérateur
référençant le caractère qui vient d'être inséré afin de permettre de récupérer ce nouvel itérateur
pour les opérations ultérieures.
Exemple 14-8. Insertion de caractères dans une chaîne
#include <iostream> #include <string> using namespace std; int main(void) { string s = "abef"; // Insère un 'c' et un 'd' : s.insert(2, "cdze", 2); // Idem pour 'g' et 'h', mais avec une basic_string : string gh = "gh"; s.insert(6, gh); cout << s << endl; return 0; }
Il existe également trois surcharges de la méthode
erase
, dont le but est de supprimer des caractères dans une chaîne en décalant
les caractères suivant les caractères supprimés pour remplir les positions ainsi libérées.
La première méthode erase
prend en premier paramètre la position du premier
caractère et le nombre des caractères à supprimer. La deuxième méthode fonctionne de manière similaire,
mais prend en paramètre l'itérateur de début et l'itérateur de fin de la sous-chaîne de caractères qui
doit être supprimée. Enfin, la troisième version de erase
permet de supprimer
un unique caractère, dont la position est spécifiée encore une fois par un itérateur. Ces deux dernières
méthodes renvoient l'itérateur référençant le caractère suivant le dernier caractère qui a été supprimé
de la chaîne. S'il n'y avait pas de caractères après le dernier caractère effacé, l'itérateur de fin
de chaîne est renvoyé.
Enfin, il existe une méthode dédiée pour l'effacement complet
de la chaîne de caractères contenue dans une basic_string. Cette méthode est la méthode
clear
.
Exemple 14-9. Suppression de caractères dans une chaîne
#include <iostream> #include <string> using namespace std; int main(void) { string s = "abcdeerrfgh"; // Supprime la faute de frappe : s.erase(5,3); cout << s << endl; // Efface la chaîne de caractères complète : s.clear(); if (s.empty()) cout << "Vide !" << endl; return 0; }
Comme pour l'insertion de chaînes de caractères, il existe
tout un jeu de fonctions permettant d'effectuer un remplacement d'une partie de la chaîne de caractères
stockée dans les basic_string par une autre chaîne de caractères. Ces méthodes sont nommées
replace
et sont tout à fait similaires dans le principe aux méthodes
insert
. Cependant, contrairement à celles-ci, les méthodes replace
prennent un paramètre supplémentaire pour spécifier la longueur ou le caractère de fin de la sous-chaîne
à remplacer. Ce paramètre doit être fourni juste après le premier paramètre, qui indique toujours
le caractère de début de la sous-chaîne à remplacer. Il peut être de type size_type
pour les versions de replace
qui travaillent avec des indices, ou être un itérateur,
pour les versions de replace
qui travaillent avec des itérateurs. Les autres
paramètres des fonctions replace
permettent de décrire la chaîne de remplacement,
et fonctionnent exactement comme les paramètres correspondants des fonctions insert
.
Exemple 14-10. Remplacement d'une sous-chaîne dans une chaîne
#include <iostream> #include <string> using namespace std; int main(void) { string s = "abcerfg"; // Remplace le 'e' et le 'r' par un 'd' et un 'e' : s.replace(3, 2, "de"); cout << s << endl; return 0; }
Dans le même ordre d'idée que le remplacement, on trouvera
la méthode swap
de la classe basic_string, qui permet d'intervertir
le contenu de deux chaînes de caractères. Cette méthode prend en paramètre une référence sur la deuxième
chaîne de caractères, avec laquelle l'interversion doit être faite. La méthode swap
pourra devra être utilisée de préférence pour réaliser les échanges de chaînes de caractères, car
elle est optimisée et effectue en fait l'échange par référence. Elle permet donc d'éviter de faire
une copie temporaire de la chaîne destination et d'écraser la chaîne source avec cette copie.
Exemple 14-11. Échange du contenu de deux chaînes de caractères
#include <iostream> #include <string> using namespace std; int main(void) { string s1 = "abcd"; string s2 = "1234"; cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; // Intervertit les deux chaînes : s1.swap(s2); cout << "s1 = " << s1 << endl; cout << "s2 = " << s2 << endl; return 0; }
La comparaison des basic_string se base sur la méthode
compare
, dont plusieurs surcharges existent afin de permettre des comparaisons
diverses et variées. Les deux versions les plus simples de la méthode compare
prennent en paramètre soit une autre basic_string, soit une chaîne de caractères C classique.
Elles effectuent donc la comparaison de la basic_string sur laquelle elles s'appliquent
avec ces chaînes. Elles utilisent pour cela la méthode eq
de la classe des traits
des caractères utilisés par la chaîne. Si les deux chaînes ne diffèrent que par leur taille, la chaîne
la plus courte sera déclarée inférieure à la chaîne la plus longue.
Les deux autres méthodes compare
permettent
d'effectuer la comparaison de sous-chaînes de caractères entre elles. Elles prennent toutes les deux
l'indice du caractère de début et l'indice du caractère de fin de la sous-chaîne de la
basic_string sur laquelle elles sont appliquées, un troisième paramètre indiquant
une autre chaîne de caractères, et des indices spécifiant la deuxième sous-chaîne dans cette chaîne.
Si le troisième argument est une basic_string, il faut spécifier également l'indice de
début et l'indice de fin de la sous-chaîne. En revanche, s'il s'agit d'une chaîne C classique, la
deuxième sous-chaîne commence toujours au premier caractère de cette chaîne, et il ne faut spécifier que
la longueur de cette sous-chaîne.
La valeur renvoyée par les méthodes compare
est de type
entier. Cet entier est nul si les deux chaînes sont strictement égales (et de même taille), négatif
si la basic_string sur laquelle la méthode compare
est appliquée est
plus petite que la chaîne passée en argument, soit en taille, soit au sens de l'ordre lexicographique,
et positif dans le cas contraire.
Exemple 14-12. Comparaisons de chaînes de caractères
#include <iostream> #include <string> using namespace std; int main(void) { const char *c1 = "bcderefb"; const char *c2 = "bcdetab"; // c2 > c1 const char *c3 = "bcderefas"; // c3 < c1 const char *c4 = "bcde"; // c4 < c1 string s1 = c1; if (s1 < c2) cout << "c1 < c2" << endl; else cout << "c1 >= c2" << endl; if (s1.compare(c3)>0) cout << "c1 > c3" << endl; else cout << "c1 <= c3" << endl; if (s1.compare(0, string::npos, c1, 4)>0) cout << "c1 > c4" << endl; else cout << "c1 <= c4" << endl; return 0; }
Bien entendu, les opérateurs de comparaison classiques sont également définis afin de permettre des comparaisons simples entre chaîne de caractères. Grâce à ces opérateurs, il est possible de manipuler les basic_string exactement comme les autres types ordonnés du langage. Plusieurs surcharge de ces opérateurs ont été définies et travaillent avec les différents types de données avec lesquels il est possible pour une basic_string de se comparer. L'emploi de ces opérateurs est naturel et ne pose pas de problèmes particuliers.
Note : Toutes ces comparaisons se basent sur l'ordre lexicographique du langage C. Autrement dit, les comparaisons entre chaînes de caractères ne tiennent pas compte de la locale et des conventions nationales. Elles sont donc très efficaces, mais ne pourront pas être utilisées pour comparer des chaînes de caractères humainement lisibles. Vous trouverez de plus amples renseignements sur la manière de prendre en compte les locales dans les comparaisons de chaînes de caractères dans le Chapitre 16.
Les opérations de recherche dans les chaînes de caractères constituent une des fonctionnalités des chaînes les plus courantes. Elles constituent la plupart des opérations d'analyse des chaînes, et sont souvent le pendant de la construction et la concaténation de chaînes. La classe basic_string fournit donc tout un ensemble de méthodes permettant d'effectuer des recherches de caractères ou de sous-chaînes dans une basic_string.
Les fonctions de recherche sont toutes surchargées afin de permettre de spécifier la position à partir de laquelle la recherche doit commencer d'une part, et le motif de caractère à rechercher. Le premier paramètre indique toujours quel est ce motif, que ce soit une autre basic_string, une chaîne de caractères C classique ou un simple caractère. Le deuxième paramètre est le numéro du caractère de la basic_string sur laquelle la méthode de recherche s'applique et à partir duquelle elle commence. Ce deuxième paramètre peut être utilisé pour effectuer plusieurs recherches successives, en repartant de la dernière position trouvée à chaque fois. Lors d'une première recherche ou lors d'une recherche unique, il n'est pas nécessaire de donner la valeur de ce paramètre, car les méthodes de recherche utilisent la valeur par défaut qui convient (soit le début de la chaîne, soit la fin, selon le sens de recherche utilisé par la méthode). Les paramètres suivants permettent de donner des informations complémentaires sur le motif à utiliser pour la recherche. Il n'est utilisé que lorsque le motif est une chaîne de caractères C classique. Dans ce cas, il est en effet possible de spécifier la longueur du motif dans cette chaîne.
Les différentes fonctions de recherche disponibles sont présentées dans le tableau suivant :
Tableau 14-1. Fonctions de recherche dans les chaînes de caractères
Méthode | Description |
---|---|
find | Cette méthode permet de rechercher la sous-chaîne correspondant au motif passé en paramètre dans la basic_string sur laquelle elle est appliquée. Elle retourne l'indice de la première occurrence de ce motif dans la chaîne de caractères, ou la valeur npos si le motif n'y apparaît pas. |
rfind | Cette méthode permet d'effectuer
une recherche similaire à celle de la méthode |
find_first_of | Cette méthode permet de rechercher la première occurrence d'un des caractères présents dans le motif fourni en paramètre. Il ne s'agit donc plus d'une recherche de chaîne de caractères, mais de la recherche de tous les caractères d'un ensemble donné. La valeur retournée est l'indice du caractère trouvé, ou la valeur npos si aucun caractère du motif n'est détecté dans la chaîne. |
find_last_of | Cette méthode est à la méthode
|
find_first_not_of | Cette méthode travaille en logique
inverse par rapport à la méthode |
find_last_not_of | Cette méthode effectue le même
travail que la méthode |
Exemple 14-13. Recherches dans les chaînes de caractères
#include <iostream> #include <string> using namespace std; int main(void) { string s = "Bonjour tout le monde !"; // Recherche le mot "monde" : string::size_type pos = s.find("monde"); cout << pos << endl; // Recherche le mot "tout" en commençant par la fin : pos = s.rfind("tout"); cout << pos << endl; // Décompose la chaîne en mots : string::size_type debut = s.find_first_not_of(" \t\n"); while (debut != string::npos) { // Recherche la fin du mot suivant : pos = s.find_first_of(" \t\n", debut); // Affiche le mot : if (pos != string::npos) cout << s.substr(debut, pos - debut) << endl; else cout << s.substr(debut) << endl; debut = s.find_first_not_of(" \t\n", pos); } return 0; }
Note : Toutes ces fonctions de recherche utilisent l'ordre lexicographique du langage C pour effectuer leur travail. Elles peuvent donc ne pas convenir pour effectuer des recherches dans des chaînes de caractères saisies par des humains, car elles ne prennent pas en compte la locale et les paramètres nationaux de l'utilisateur. La raison de ce choix est essentiellement la recherche de l'efficacité dans la bibliothèque standard. Nous verrons dans le Chapitre 16 la manière de procéder pour prendre en compte les paramètres nationaux au niveau des chaînes de caractères.
Pour terminer ce tour d'horizon des chaînes de caractères, signalons que la bibliothèque standard C++ fournit des opérateurs permettant d'effectuer des écritures et des lectures sur les flux d'entrée / sortie. Les opérateurs '<<' et '>>' sont donc surchargés pour les basic_string, et permettent de manipuler celles-ci comme des types normaux. L'opérateur '<<' permet d'envoyer le contenu de la basic_string sur le flux de sortie standard. L'opérateur '>>' lit les données du flux d'entrée standard, et les affecte à la basic_string qu'il reçoit en paramètre. Il s'arrête dès qu'il rencontre le caractère de fin de fichier, un espace, ou que la taille maximale des basic_string a été atteinte (cas improbable) :
#include <iostream> #include <string> using namespace std; int main(void) { string s1, s2; cin >> s1; cin >> s2; cout << "Premier mot : " << endl << s1 << endl; cout << "Deuxième mot : " << endl << s2 << endl; return 0; }
Cependant, ces opérateurs peuvent ne pas s'avérer suffisants. En effet, l'une des principales difficultés dans les programmes qui manipulent des chaînes de caractères est de lire les données qui proviennent d'un flux d'entrée ligne par ligne. La notion de ligne n'est pas très claire, et dépend fortement de l'environnement d'exécution. La bibliothèque standard C++ suppose, quant à elle, que les lignes sont délimitées par un caractère spécial servant de marqueur spécial. Généralement, ce caractère est le caractère '\n', mais il est également possible d'utiliser d'autres séparateurs.
Pour simplifier les opérations de lecture de textes constitués de lignes,
la bibliothèque fournit la fonction getline
. Cette fonction prend en premier paramètre
le flux d'entrée sur lequel elle doit lire la ligne, et la basic_string dans laquelle elle
doit stocker cette ligne en deuxième paramètre. Le troisième paramètre permet d'indiquer le caractère
séparateur de ligne. Ce paramètre est facultatif, car il dispose d'une valeur par défaut qui correspond
au caractère de fin de ligne classique '\n'.
Précédent | Sommaire | Suivant |
Notion de complexité algorithmique | Niveau supérieur | Les types utilitaires |