IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Les chaînes de caractères
        Y a-t-il un type chaîne de caractères en C++ ?
        Quels sont les avantages de string par rapport à char* ?
        Utiliser string n'est-il pas plus lent ?
        Quelle est la différence entre char*, const char* et char const * ?
        Quelle est la différence entre #include <string> et #include <string.h> ?
        Quelle est la différence entre string::length() et string::size() ?
        Quelle différence entre string::size() et string::capacity() ?
        Comment convertir un char* en un string ?
        Comment convertir une string en char* ?
        Comment convertir un nombre en une string ?
        Comment convertir une string en un entier ?
        Comment convertir n'importe quel type d'objet en string ?
        Comment convertir une string en un objet de n'importe quel type ?
        Comment déterminer si une chaîne contient une valeur d'un certain type ?
        Comment fonctionne le test de réussite de conversion if ( str >> num ) ?
        Quelle est la différence entre string::data() et string::c_str() ?
        Quelles précautions faut-il prendre avec string::c_str() et string::data() ?
        Quelle est la différence entre string::find() et string::find_first_of() ?
        Comment manipuler des chaînes de caractères Unicode ?
        Comment manipuler des chaînes de caractères ne tenant pas compte de la casse ?
        Comment convertir une string en minuscules / majuscules ?
        Comment inverser le contenu d'une chaîne ?
        Comment découper une chaîne en fonction d'un séparateur ?
        Dans quels cas ne faut-il pas utiliser string ?
        Comment tester des chaînes de caractères dans un bloc switch ?
        Comment effectuer les conversions de texte ASCII <-> Unicode ?
        [Piège] Comment initialiser/affecter un nombre à une string ?
        [Exemple] Comment supprimer des caractères d'une string ?
        [Exemple] Comment manipuler un nom de fichier avec string ?
        [Exemple] Comment manipuler un tableau de string ?

rechercher
precedent    sommaire    suivant    telechargermiroir


Y a-t-il un type chaîne de caractères en C++ ?
auteur : Aurélien Regat-Barrel
Le C++ standard possède son propre type chaîne de caractères : std::string. Celui-ci est déclaré dans l'en-tête standard <string>.

#include <iostream> // pour std::cout
#include <string>   // pour std::string

int main()
{
    std::string message = "Hello"; // création de la chaîne "Hello"
    message += " World !";         // concaténation de " Word !"

    std::cout << message << '\n';  // affichage de "Hello World !"
}
string est le type chaîne de caractères standard du langage C++. Il est donc judicieux de l'utiliser en priorité sur les char * qui sont hérités du langage C. Pour plus de détails à ce sujet, vous pouvez lire Quels sont les avantages de std::string par rapport à char * ?.


Quels sont les avantages de string par rapport à char* ?
auteur : Aurélien Regat-Barrel
Au delà du simple conteneur de caractères, string est aussi et avant tout l'interface chaîne de caractères de la bibliothèque standard. Autrement dit en utilisant string vous bénéficiez des très nombreuses autres fonctionnalités de la bibliothèque standard, ce qui est plus délicat avec les char*. Par exemple, il est possible de lire une ligne d'un fichier sans avoir à se préoccuper de sa taille.
Un travail important de gestion et de vérification est fait en toute transparence, ce qui rend le code plus maintenable. string est donc beaucoup plus simple et sûre d'utilisation que les nombreuses fonctions utilisant les char*.


Utiliser string n'est-il pas plus lent ?
auteur : Aurélien Regat-Barrel
Programmer en utilisant std::string est incontestablement plus rapide, plus lisible et plus sûr que de programmer en utilisant les antiques char*. Si votre but est d'écrire un programme qui marche et qui soit facile à maintenir, utiliser string est un bien meilleur choix. Il est en effet tentant de dire qu'utiliser les char* fait surtout planter le programme plus vite.
Il faut aussi relativiser la possible lenteur de string par le fait que l'écart est très souvent sans conséquence pour l'utilisateur. Même si lire et afficher une chaîne de caractères est deux fois plus lent avec les string qu'avec les char*, l'écart est dans ce cas dérisoire voire non mesurable. Néanmoins, il est des cas où une application manipule un très grand nombre de chaînes de caractères, et on peut alors, à juste titre, se poser la question. La réponse n'est pas évidente. Tout d'abord, le premier point en faveur des string est que celles-ci connaissent en permanence leur longueur (fonction size() ou length()), qui n'a donc pas besoin d'être calculée, contrairement aux char* ou il faut systématiquement effectuer un appel à strlen(). Ainsi, plus la longueur des chaînes manipulées est importante, et plus string se révèlera performante vis à vis des char*.
Un autre point important concerne la manière dont sont gérées les char*. Beaucoup de programmeurs ne veulent pas s'embêter et définissent généralement une taille commune à toutes leurs chaînes de caractères.

#define MAX_SIZE 100

void Exemple( void )
{
    char nom[ MAX_SIZE ];

    printf( "Veuillez entrer votre nom : " );
    fgets( stdin, nom, MAX_SIZE );
}
Là aussi, string est meilleur car non seulement le problème (inévitable) de la fois où cette taille limite sera atteinte ne se pose pas, mais aussi parce que ce code C provoque une perte plus ou moins importante de mémoire (typiquement, MAX_SIZE vaut 100 voire 1000, c'est à dire que même si une chaîne ne comporte que 10 caractères, elle occupera 100 ou 1000 octets en mémoire!). La comparaison doit donc être faite avec une gestion dynamique de char*, chose laborieuse à gérer en C et faite de manière transparente et sûre par string.
Enfin, il est difficile de généraliser sur les string dans la mesure où celles-ci sont spécifiques à un compilateur (et même souvent une version de compilateur) et ce qui est vrai pour une implémentation ne l'est pas forcément pour une autre (d'autant plus que la capacité d'optimisation du compilateur entre aussi en jeu). Ainsi, par exemple, la classe string de Visual C++ 6 implémente le Copy On Write (pas de copie réelle de contenu entre 2 string, mais un partage via un comptage de références) qui permet d'effectuer des affectations très rapides entre string. La version 7 de ce compilateur n'implémente plus cette fonctionnalité mais intègre un petit buffer de 16 octets destiné à contenir directement les chaînes de petite taille. Cela permet de se passer d'allocation dynamique et donc d'augmenter les performances qui sont du coup égales à celles d'un tableau de char (dans le cas de petites chaînes).
La comparaison de performances entre string et char* est donc difficile, et elle ne peut être qu'au cas par cas. Bien souvent, l'écart de performance entre les deux est de l'ordre de quelques pour cent.
Vous l'aurez compris, la performance est loin d'être le seul critère, aussi faites confiance aux nombreux développeurs de talent qui ont développé le C++ et utilisez le type chaînes de caractères qui lui est propre : string.


Quelle est la différence entre char*, const char* et char const * ?
auteur : Luc Hermitte
const char * et char const * ont la même signification : un pointeur sur un caractère constant. La règle est que le const s'applique toujours sur ce qui le précède. S'il n'y a rien avant, alors on inverse sa position avec ce qui est juste après.
Utiliser const signifie qu'il ne faut pas modifier le(s) caractère(s) référencé(s) par le pointeur.
Donc, typiquement, une fonction qui prend un char* déclare qu'elle modifiera le contenu du buffer pointé par le pointeur (accès en écriture). Une fonction qui prend un const char* déclare qu'elle va lire le contenu du buffer sans le modifier (accès en lecture seule).


Quelle est la différence entre #include <string> et #include <string.h> ?
auteur : Aurélien Regat-Barrel
<string> et <string.h> sont deux fichiers d'en-tête totalement différents. Le premier est l'en-tête standard C++ qui définit le type std::string. Le second est un en-tête hérité du langage C qui définit diverses fonctions C de manipulation de chaînes de caractères. Il est à noter qu'inclure <string.h> est obsolète, il convient d'inclure <cstring> à la place.
A ce sujet lire aussi Quelle est le différence entre #include <iostream.h> et #include <iostream> ?.


Quelle est la différence entre string::length() et string::size() ?
auteur : Aurélien Regat-Barrel
Aucune! Ces deux fonctions renvoient toutes les deux la longueur de la chaîne. En fait, length() retourne size(), mais on est libre d'utiliser l'une ou l'autre.
Voir aussi Quelle différence entre string::size() et string::capacity() ?


Quelle différence entre string::size() et string::capacity() ?
auteur : Aurélien Regat-Barrel
size renvoie le nombre de caractères contenus dans la chaîne (par exemple 4 pour "abcd") et capacity la capacité de stockage de la chaîne, c'est à dire le nombre total de caractères qu'elle peut stocker sans nécessiter une réallocation. capacity est donc toujours au moins égal à size, et peut être plus élevé, en particulier si vous faites un appel à reserve.

#include <string>
#include <iostream>

int main()
{
    using namespace std;

    string s; // la chaîne est vide
    cout << "size = " << s.size() << '\t'  // size = 0 
         << "capacity = " << s.capacity()  // capacity = 15
         << '\n';

    // on sait que l'on va faire de nombreuses concaténations,
    // alors on réserve de l'espace pour éviter de multiples allocations
    s.reserve( 26 );
    cout << "size = " << s.size() << '\t'  // size = 0 
         << "capacity = " << s.capacity()  // capacity = 31
         << '\n';

    // concaténer les 26 lettres de l'alphabet
    for ( char c = 'A'; c <= 'Z'; ++c )
    {
        s += c;
    }

    cout << "size = " << s.size() << '\t'  // size = 26 
         << "capacity = " << s.capacity()  // capacity = 31
         << '\n';

    s = "une assez longue chaîne qui oblige a faire une allocation";
    cout << "size = " << s.size() << '\t'  // size = 57
         << "capacity = " << s.capacity()  // capacity = 63
         << '\n';
}
La valeur retournée par capacity peut varier d'un compilateur à l'autre, ou plutôt d'une implémentation de la STL à l'autre. C'est pourquoi il est fort probable que vous n'obteniez pas les mêmes résultats si votre compilateur n'est pas celui utilisé pour ce test (Visual C++ .Net 2003). Par contre quelque soit votre compilateur les valeurs retournées par size devraient être les même.
Voir aussi Quelle est la différence entre string::length() et string::size() ?


Comment convertir un char* en un string ?
auteurs : JEG, LFE, Aurélien Regat-Barrel
En utilisant tout simplement le constructeur ou l'opérateur d'affectation de la classe string :

#include <string>

// construction
std::string str1 = "coucou";
std::string str2( "coucou" );
// affectation
std::string str3;
str3 = "coucou";
str3.assign( "coucou" );

Comment convertir une string en char* ?
auteurs : LFE, Aurélien Regat-Barrel
Même en C++ on est parfois obligé d'utiliser des char *. Pour obtenir une chaîne de caractères C non modifiable (const char *), il suffit d'appeler la fonction c_str() de string.

string file_name = "fichier.txt";
// ouverture du fichier
ifstream file( file_name.c_str() ); // le constructeur de std::ifstream n'accepte pas de string
La chaîne renvoyée est qualifiée comme non modifiable via le mot-clé const. Il ne faut pas chercher à la modifier. A ce sujet, lire les questions Quelle est la différence entre char*, const char* et char const * ? et Quelles précautions faut-il prendre avec string::c_str() et string::data() ?.
Pour obtenir une chaîne de type C modifiable, il faut créer une copie de la string.

#include <string>
#include <cstring>
#include <iostream>

int main()
{
    using namespace std;

    string str = "une chaîne de caractères";
    // créer le buffer pour copier la chaîne
    size_t size = str.size() + 1;
    char * buffer = new char[ size ];
    // copier la chaîne
    strncpy( buffer, str.c_str(), size );
    // utiliser le buffer
    cout << buffer << '\n'; // "une chaîne de caractères" 
    // libérer la mémoire
    delete [] buffer;
}

Comment convertir un nombre en une string ?
auteur : Aurélien Regat-Barrel
L'utilisation d'un objet ostringstream permet de convertir un entier en une string :

#include <sstream>

int main()
{    
    // créer un flux de sortie
    std::ostringstream oss;
    // écrire un nombre dans le flux
    oss << 10;
    // récupérer une chaîne de caractères
    std::string result = oss.str();
}
Pour une solution générique à ce problème, consultez Comment convertir n'importe quel objet en string ?. Pour éviter les erreurs, lire aussi [Piège] Comment initialiser/affecter un nombre à une string ? .


Comment convertir une string en un entier ?
auteur : Aurélien Regat-Barrel
En utilisant istringstream:

#include <sstream>

int main()
{
    // créer un flux à partir de la chaîne à convertir
    std::istringstream iss( "10" );
    // convertir en un int
    int nombre;
    iss >> nombre; // nombre vaut 10
}
Vous pouvez consulter Comment convertir une string en un objet de n'importe quel type ? pour une utilisation plus générique de cette solution.

lien : Comment déterminer si une chaîne contient une valeur d'un certain type ?

Comment convertir n'importe quel type d'objet en string ?
auteur : Aurélien Regat-Barrel
L'utilisation de ostringstream permet de convertir en une string n'importe quel objet pour lequel l'opérateur ostream::operator <<() a été défini. Il est donc possible de créer une fonction générique de conversion de n'importe quel objet en une string grâce à l'utilisation des templates :

#include <sstream>  

template<typename T>
std::string to_string( const T & Value )
{
    // utiliser un flux de sortie pour créer la chaîne
    std::ostringstream oss;
    // écrire la valeur dans le flux
    oss << Value;
    // renvoyer une string
    return oss.str();
}

int main()
{    
    std::string num = to_string( 10 );
}

Comment convertir une string en un objet de n'importe quel type ?
auteur : Aurélien Regat-Barrel
L'utilisation de istringstream permet de convertir une string en n'importe quel type pour lequel l'opérateur istream::operator >>() a été défini. Il est donc possible de créer une fonction générique de conversion d'une string en un autre type grâce à l'utilisation des templates :

#include <sstream>  

template<typename T>
bool from_string( const std::string & Str, T & Dest )
{
    // créer un flux à partir de la chaîne donnée
    std::istringstream iss( Str );
    // tenter la conversion vers Dest
    return iss >> Dest != 0;
}

int main()
{    
    int dix;
    from_string( "10", dix );
}
Pour la conversion d'une string en une chaîne de caractères constante (const char *), utilisez la fonction membre c_str() de std::string.
Pour la conversion d'une string en une chaîne de caractères modifiable, consultez Comment convertir une string en char* ? ? Pour des détails sur le principe de la conversion avec l'opérateur >>, consultez Comment fonctionne le test de réussite de conversion if ( str >> num ) ?

lien : Comment déterminer si une chaîne contient une valeur d'un certain type ?

Comment déterminer si une chaîne contient une valeur d'un certain type ?
auteurs : LFE, Aurélien Regat-Barrel
Il suffit de tester le résultat de l'opération de conversion, comme le fait la fonction générique from_string de la question Comment convertir une string en un objet de n'importe quel type ?. Ce principe est expliqué dans Comment fonctionne le test de réussite de conversion if ( str >> num ) ?

#include <sstream>  

bool is_float( const std::string & Str )
{
    // créer un flux à partir de la chaîne donnée
    std::istringstream iss( Str );
    // créer un objet temporaire pour la conversion
    float tmp;
    // tenter la conversion et
    // vérifier qu'il ne reste plus rien dans la chaîne
    return ( iss >> tmp ) && ( iss.eof() );
}

int main()
{
    is_float( "10.0" ); // vrai
    is_float( "abcd" ); // faux
    is_float( "10.0abcd" ); // faux grâce au  test
}
Après avoir converti avec succès Str en un float, on tente d'extraire une chaîne, opération qui ne peut échouer que s'il ne reste plus rien une fois le float extrait. Cela permet de faire échouer le troisième test avec "10.0abcd".
Cette solution spécifique aux float peut aisément être généralisée grâce aux templates :

#include <sstream>  

template<typename T>
bool is_of_type( const std::string & Str )
{
    // créer un flux à partir de la chaîne donnée
    std::istringstream iss( Str );
    // créer un objet temporaire pour la conversion
    T tmp;
    // tenter la conversion et
    // vérifier qu'il ne reste plus rien dans la chaîne
    return ( iss >> tmp ) && ( iss.eof() );
}

int main()
{
    is_of_type<float>( "10.5" ); // vrai
    is_of_type<int>( "10.5" ); // faux grâce au  test
}
L'utilisation des templates nécessite ici leur instanciation explicite, c'est à dire de spécifier au moment de l'utilisation de la fonction le type dont on souhaite vérifier la conversion depuis une string. Comme le montre cet exemple, la réussite ou non de la conversion est déterminée par le comportement de l'opérateur >> pour le type donné. La fonction is_of_type devrait donc plutôt s'appeler is_convertible_in_type. Pour tester avec plus de rigueur le contenu d'une string il faut donc se tourner vers une autre solution, comme l'utilisation d'expressions régulières.

lien : Comment convertir une string en un objet de n'importe quel type ?

Comment fonctionne le test de réussite de conversion if ( str >> num ) ?
auteur : Aurélien Regat-Barrel
Pour que le code suivant compile :

istringstream s( "10" );
int n;
if ( s >> n )
{
}
Le compilateur cherche à convertir l'expression s >> n en un booléen. Il commence par rechercher un opérateur applicable et trouve celui hérité de istream qui accepte un int. Cela revient donc à avoir le code suivant :

istringstream s( "10" );
int n;
if ( s.operator >>( n ) )
{
}
Cet opérateur retourne une référence sur le flux utilisé, c'est à dire ici la variable s. Le compilateur essaye alors de convertir cette référence (qui correspond à s après l'appel de l'opérateur >>, donc après avoir tenté la conversion en int) en booléen. istream ne définit pas d'opérateur de conversion vers bool, mais une conversion implicite est possible grâce à l'opérateur operator void*(). Ce dernier retourne un pointeur nul si les indicateurs d'échec du flux sont positionnés, ce qui est le cas en cas d'échec de conversion. L'ensemble du processus effectué par le compilateur correspond donc aux appels explicites suivants :

istringstream s( "10" );
int n;
if ( s.operator >>( n ).operator void*() != 0 )
{
}

Quelle est la différence entre string::data() et string::c_str() ?
auteur : Aurélien Regat-Barrel
c_str() retourne un pointeur sur une chaîne de caractères constante terminée par le caractère nul (chaîne de caractères C). data() retourne aussi un pointeur sur une chaîne de caractères constante, mais la présence du caractère terminal nul n'est pas exigée donc non garantie. Donc c_str() renvoie un pointeur constant sur un buffer contenant size() + 1 caractères et data() sur un buffer de size() caractères.


Quelles précautions faut-il prendre avec string::c_str() et string::data() ?
auteur : Aurélien Regat-Barrel
Il ne faut faire aucune hypothèse quant à la façon dont est implémentée la classe string. Le comportement de cette dernière est spécifique à presque chaque version de compilateur C++ existant.
Par exemple, les caractères peuvent ne pas être stockés en interne de manière contiguë (on peut envisager un système de concaténation rapide via un chaînage de sous chaînes de caractères).
Ou encore, certaines implémentations utilisent le Copy On Write (COW) qui implique que plusieurs objets string peuvent en interne partager le même espace mémoire pour stocker leurs caractères.
Le seul point commun à toutes ces implémentations est que l'on est assuré que le pointeur retourné par c_str() ou data() désigne une chaîne de caractères contigus. Mais rien n'empêche celui-ci de pointer vers une copie créée pour l'occasion !
C'est pourquoi il est très important de ne jamais modifier la chaîne retournée par ces fonctions.
Un autre point important est que ce pointeur peut être invalidé suite à une modification, et que sa durée de vie n'excède pas celle de l'objet associé. Le code suivant illustre ces deux points :

#include <string>

int main()
{
    using std::string;

    string str( "Hello" );
    const char * c_str = str.c_str(); // ok
    
    // erreur 1 : invalidation de pointeur suite à une modification
    str += " World!"; // concaténer " World!" à "Hello"
    // maintenant c_str est invalide, et ne doit plus être utilisé!

    c_str = str.c_str();
    // erreur 2 : l'utilisation de [] peut aussi invalider c_str
    char c = str[ 0 ]; // ok, accès en lecture seulement
    str[ 0 ] = 'A'; // modification d'un caractère
    // maintenant c_str est invalide, et ne doit plus être utilisé!

    // erreur 3 : invalidation de pointeur suite à une destruction
    {
        // variable temporaire à ce bloc
        string str2( "Temporaire" );
        c_str = str2.c_str(); // ok
    } // fin du bloc : str2 est détruite
    // maintenant c_str est invalide car la chaîne pointée a été détruite!    
}
Le code ci-dessus compile parfaitement, mais provoque un certain nombre d'erreurs d'exécution qui varie en fonction de l'implémentation de string utilisée.


Quelle est la différence entre string::find() et string::find_first_of() ?
auteur : Aurélien Regat-Barrel
string (mais aussi <algorithm>) possède deux fonctions de recherche qui toutes les deux recherchent la première occurrence d'un élément :

string s = "abcdef";
if ( s.find( 'c' ) == s.find_first_of( 'c' ) )
{
    // ce test est vrai
}
La différence entre les deux est que find recherche la première occurrence d'un caractère ou d'une chaîne de caractères dans une string tandis que find_first_of recherche le premier caractère de la chaîne qui soit égal à l'un de ceux donnés en paramètre. Donc dans le cas de la recherche d'un caractère il n'y a pas de différence entre les deux, mais ces deux fonctions sont très différentes si leur argument est une chaîne de caractères. find considèrera cette chaîne comme une sous chaîne à rechercher, et find_first_of comme une liste de caractères à rechercher.

string s = "abcba";

cout << s.find( "ba" ) << '\n'; // affiche 3
cout << s.find_first_of( "ba" ) << '\n'; // affiche 0
L'appel à find retourne la position de la sous chaîne "ba" dans "abcba" (3° position). L'appel à find_first_of renvoie la position de la première occurrence de n'importe lequel des caractères passés en paramètre, c'est à dire ici 0 (première lettre de "abcba" = a qui figure bien dans la liste "ba").


Comment manipuler des chaînes de caractères Unicode ?
auteur : Aurélien Regat-Barrel
De la même manière que les chaînes ANSI, mais au moyen du type std::wstring qui est défini dans le fichier d'en-tête standard <string>. Il s'agit d'une spécialisation de std::basic_string pour le type wchar_t (caractère Unicode) au même titre que std::string l'est pour le type char.

#include <string>
#include <iostream>

int main()
{
    std::wstring s = L"Chaîne unicode";
    wchar_t c = s[ 0 ]; // c = L'C'
    std::wcout << s;
}
Notez qu'une version Unicode existe aussi pour les flux, afin de les rendre utilisables avec wstring et wchar_t. Ainsi, on utilisera wcout pour l'affichage, wcin pour la saisie, wifstream pour la lecture de fichiers, ...
Cependant, en ce qui concerne les fichiers, les caractères wchar_t sont convertis de manière transparente en char au moment de l'écriture. Le C++ standard ne permet pas en effet de manipuler des fichiers Unicode.

Pour réaliser les conversions string <-> wstring, voir Comment effectuer les conversions de texte ASCII <-> Unicode ?.


Comment manipuler des chaînes de caractères ne tenant pas compte de la casse ?
auteur : Laurent Gomila
Le type standard pour manipuler des chaînes en C++ est std::string. Mais si l'on regarde de plus près, on s'aperçoit qu'il ne s'agit en fait que qu'un typedef :

namespace std
{
    typedef basic_string<char, char_traits<char>, allocator<char> > string;
}
Le premier paramètre représente le type des caractères manipulés, le troisième est l'allocateur et nous importe peu ici. Ce qui est intéressant, c'est le second paramètre qui définit les opérations de base (recherches, comparaisons, ...) sur le type manipulé. Ainsi, pour créer des chaînes de caractères ne tenant pas compte de la casse (différence entre majuscules et minuscules) il suffit de créer un char_traits perso :

struct ci_char_traits : public std::char_traits<char>
// on dérive du char_traits par défaut, ainsi on hérite des fonctions que nous ne voulons pas redéfinir
{
    static bool eq(char c1, char c2)
    {
        return toupper(c1) == toupper(c2);
    }

    static bool ne(char c1, char c2)
    {
        return toupper(c1) != toupper(c2);
    }

    static bool lt(char c1, char c2)
    {
        return toupper(c1) < toupper(c2);
    }

    static int compare(const char* s1, const char* s2, size_t n)
    {
        return memicmp(s1, s2, n); // si disponible sur votre compilateur
    }

    static const char* find(const char* s, int n, char a)
    {
        while ((n-- > 0) && (toupper(*s) != toupper(a)))
            ++s;

        return s;
    }
};
Il ne nous reste plus ensuite qu'à créer le basic_string correspondant :

typedef std::basic_string<char, ci_char_traits> ci_string;
L'avantage est que celui-ci se manipule exactement comme un std::string normal :

ci_string s1 = "salut";
ci_string s2 = "SAluT";

cout << (s1 == s2); // affiche "1" (vrai)
Pour des informations plus complètes, vous pouvez jeter un oeil à en l'item n°29 des GOTW (Guru Of The Week).


Comment convertir une string en minuscules / majuscules ?
auteurs : Aurélien Regat-Barrel, Laurent Gomila
D'une manière générale, le problème de la conversion d'une chaîne majuscule en minuscule (ou inversement) est bien plus complexe qu'il n'y paraît. C'est un sujet qui débordre du cadre de cette FAQ, mais voici un rapide aperçu des problèmes soulevés:

  • Les caractères accentués posent problème. En général, on souhaite convertir le mot "fête" en "FETE" et non en "FÊTE" (ce qui est assez simple à faire), et inversement le mot "FETE" en "fête", ce qui implique de convertir chaque caractère en fonction du contexte et du mot dans lequel il apparait (notion d'orthographe). Quelques fois, même par un humain, il est impossible de trancher (par exemple avec la phrase "UN INTERNE TUE A L'ASILE PSYCHIATRIQUE", est-ce un interne / un interné qui tue / a été tué ?)
  • Certains caractères ne doivent pas être convertis en minuscule, comme celui du début d'une phrase ou encore d'un nom propre. Par exemple, la phrase "MERCI M. DUPONT" devrait normalement être convertie en "Merci M. Dupont". Bien évidément c'est une tâche extrêmement complexe à réaliser de manière automatisée
  • Dans certaines langues, un caractère majuscule ne correspond pas forcément à un seul caractère minuscule, et inversement. Ainsi, en Allemand par exemple, la combinaison de majuscules SS peut correspondre soit à la combinaison de minuscule ss, soit à l'unique lettre minuscule ß. Et quelques fois les deux sont même permis (Masse/Maße). Ajoutez à cela que les Suisses et les Autrichiens ont des règles différentes, et l'on comprend mieux la complexité du problème.
Si l'on se recentre sur le C++, la solution classique (qui est aussi celle généralement attendue) est d'effectuer une conversion in-place ("en place", c'est à dire directement sur la chaîne, caractère par caractère) au moyen de toupper (pour mettre en majuscule) ou tolower (pour mettre en minuscule). std::transform est traditionnellement utilisé pour appliquer une des ces fonctions à chaque caractère d'une chaîne.

Pour convertir une string en minuscules ou en majuscules, il faut appliquer la fonction std::tolower / std::toupper à chaque caractère, ce que l'on réaliser par exemple grâce à std::transform.
Cependant, std::tolower et std::toupper attendent un paramètre compris dans l'intervalle [0...UCHAR_MAX] (ou EOF), et un char peut potentiellement être négatif (les caractères accentués, par exemple). Utiliser directement ces fonctions provoquerait donc un comportement indéfini par la norme, c'est pourquoi il faut ajouter un traitement intermédiaire, afin de convertir les caractères dans le bon type (unsigned char ici).

#include <cctype>    // pour tolower et toupper
#include <string>    // pour string
#include <iostream>  // pour cout
#include <algorithm> // pour transform

struct my_tolower
{ 
    char operator()(char c) const 
    {
        return std::tolower(static_cast<unsigned char>(c));
    } 
};

struct my_toupper
{ 
    char operator()(char c) const 
    {
        return std::toupper(static_cast<unsigned char>(c));
    } 
};

int main()
{
    std::string s("ABCDEF");

    std::transform(s.begin(), s.end(), s.begin(), my_tolower());
    std::cout << s; // affiche "abcdef"

    std::transform(s.begin(), s.end(), s.begin(), my_toupper());
    std::cout << s; // affiche "ABCDEF"

    return 0;
}
Si vous n'êtes pas familier avec l'écriture utilisée pour my_tolower et my_toupper, voir Qu'est-ce qu'un foncteur ?.

Ceux qui recherchent des explications poussées et une solution complètement fonctionnelle pour la conversion minuscules / majuscules, peuvent consulter cette discussion sur fr.comp.lang.c++.


Comment inverser le contenu d'une chaîne ?
auteur : Laurent Gomila
Le plus simple est d'utiliser la fonction std::reverse() sur la chaîne existante, ou d'en créer une nouvelle en passant un itérateur inverse au constructeur de std::string.

#include <algorithm> // pour std::reverse()
#include <string> 

std::string chaine = "Bonjour"; 

// Inversion directe de la chaîne 
std::reverse(chaine.begin(), chaine.end()); 

// Autre méthode : création d'une chaîne inversée 
std::string inverse( chaine.rbegin(), chaine.rend() );

Comment découper une chaîne en fonction d'un séparateur ?
auteur : Aurélien Regat-Barrel
std::string ne dispose pas de fonction équivalente à la fonction C standard strtok(). Il existe de nombreuses façons de réaliser un équivalent à cette fonction. L'une des solutions les plus simples et de procéder au découpage sur un istringstream au moyen de std::getline():

#include <sstream>
#include <string>
#include <iostream>

using namespace std;

int main()
{
    istringstream iss( "mot1;mot2;mot3;mot4" );
    string mot;
    while ( std::getline( iss, mot, ';' ) )
    {
        cout << mot << '\n';
    }
}
Par défaut getline() sert à extraire des chaînes délimitées par un saut de ligne, mais comme le montre l'exemple précédent il est possible de spécifier un autre séparateur (ici le caractère ';').
Attention : getline() va considérer tout ce qui se trouve entre deux ';' comme étant une ligne à extraire. Cela veut dire que la phrase "mot1 mot2;;mot3" (notez le double point virgule entre mot2 et mot3) sera découpée en "mot1 mot2", "" (chaîne vide) et "mot3". Cette utilisation de getline() ne permet aussi de spécifier qu'un seul séparateur. Si vos mots sont séparés par de simples espaces, vous pouvez aussi vous inspirer de [Exemple] Comment manipuler un tableau de string ?.

Pour quelque chose de plus évolué, vous pouvez vous tourner vers en boost::tokenizer (voir Comment découper une chaîne avec boost::tokenizer ?) ou encore réaliser votre propre fonction de découpage en vous inspirant de ceci : en Breaking a C++ string into tokens.


Dans quels cas ne faut-il pas utiliser string ?
auteur : Aurélien Regat-Barrel
<string> effectue en général une allocation dynamique afin de stocker la chaîne de caractères. Cette allocation peut échouer si la mémoire fait défaut. Il faut donc être prudent avec l'utilisation de string et plus généralement des conteneurs de la STL lors de la gestion d'une exception bad_alloc.

void Exemple()
{
    try
    {
        // effectuer une allocation
    }
    catch ( std::bad_alloc & )
    {
        // ATTENTION : utiliser std::string ou tout autre conteneur standard
        // peut déclencher une nouvelle exception std::bad_alloc
    }
}
C'est pour cela que la classe de base des exceptions std::exception n'utilise pas string pour sa fonction membre what qui renvoie un message décrivant l'exception. Il ne faut donc pas chercher à le faire lorsque l'on crée sa propre classe exception.


Comment tester des chaînes de caractères dans un bloc switch ?
auteur : Aurélien Regat-Barrel
On ne peut pas. Le code suivant ne compile pas :

// fonctions de traitement
void parametre_input();
void parametre_output();
void parametre_inconnu();

// appel des fonctions de traitement
void analyse_parametre( const char * Param )
{
    switch ( Param )
    {
    case "/input":
        parametre_input(); break;

    case "/output":
        parametre_output(); break;

    default:
        parametre_inconnu();
    }
}
Il est possible d'arriver au résultat escompté en chaînant une série de if testant les différentes chaînes, mais une solution élégante en C++ consiste à utiliser std::map défini dans l'en-tête standard <map>. Une map permet d'associer un élément à une clé. Dans notre cas nous allons associer une fonction de traitement à une chaîne de caractères :

// fonctions de traitement
void parametre_input();
void parametre_output();
void parametre_inconnu();

// type pointeur de fonction de traitement
typedef void (*parametre_fct)();

void analyse_parametre( const string & Param )
{
    static map<string, parametre_fct> param_map;

    // initialiser la map si ce n'est pas fait
    if ( param_map.empty() )
    {
        param_map[ "/input" ] = parametre_input;
        param_map[ "/output" ] = parametre_output;
    }

    // rechercher la fonction associée à Param
    map<string, parametre_fct>::const_iterator i = param_map.find( Param );
    if ( i == param_map.end() )
    {
        // échec
        parametre_inconnu();
    }
    else
    {
        // appeler la fonction associée
        (i->second)();
    }
}

Comment effectuer les conversions de texte ASCII <-> Unicode ?
auteur : superspag
Les fonctions suivantes permettent, via l'utilisation de la bibliothèque standard, de transformer un texte Unicode en ASCII (narrow), et vice-versa (widen).

#include <string>
#include <locale>
#include <vector>
 
std::string narrow(const std::wstring& ws)
{
    std::vector<char> buffer(ws.size());
    std::locale loc("english");
    std::use_facet< std::ctype<wchar_t> >(loc).narrow(ws.data(), ws.data() + ws.size(), '?', &buffer[0]);
 
    return std::string(&buffer[0], buffer.size());
}
 
std::wstring widen(const std::string& s)
{
    std::vector<wchar_t> buffer(s.size());
    std::locale loc("english");
    std::use_facet< std::ctype<wchar_t> >(loc).widen(s.data(), s.data() + s.size(), &buffer[0]);
 
    return std::wstring(&buffer[0], buffer.size());
}

[Piège] Comment initialiser/affecter un nombre à une string ?
auteur : Aurélien Regat-Barrel
Le code suivant compile mais ne donne pas le résultat attendu :

string s = 0;
Ce code ne produit pas la chaîne "0". Il compile car le zéro est interprété comme un pointeur NULL. Certains compilateurs permissifs acceptent même un nombre différent de zéro, ce qui peut avoir des conséquences désastreuses (accès aléatoire à une zone mémoire).
Il n'est pas possible d'initialiser directement une string avec un nombre. Il faut convertir celui-ci en chaîne de caractères puis utiliser ce résultat. La question Comment convertir un nombre en une string ? explique comment réaliser cela.


[Exemple] Comment supprimer des caractères d'une string ?
auteur : Laurent Gomila

#include <string> 
#include <algorithm> // remove(), erase()
#include <iostream> 

// supprime toutes les occurrences du caractère C donné
void SupprimeTousLesCaracteres( std::string & Str, char C )
{ 
    Str.erase(
        std::remove( Str.begin(), Str.end(), C ),
        Str.end() ); 
}    

// supprime tous les caractères C situés au début de Str
std::string SupprimeLesPremiersCaractères( const std::string & Str, char C )
{ 
   return Str.substr(
       Str.find_first_not_of( C ) ); 
} 

int main()
{ 
    using namespace std;

    // supprimer les quote entre les mots
    string s1 = "'mot1' 'mot2' 'mot3' 'mot4'";
    SupprimeTousLesCaracteres( s1, '\'' );
    cout << s1 << '\n'; // affiche "mot1 mot2 mot3 mot4"

    // enlever les espaces gênants au début de la chaîne
    string s2 = "     exemple"; 
    cout << SupprimeLesPremiersCaractères( s2, ' ' ); // affiche "exemple"
} 
Notez que le premier exemple (SupprimeTousLesCaracteres) est un cas typique d'utilisation de l'idiome erase-remove qui consiste à combiner la fonction remove() avec la fonction erase() afin de supprimer les éléments répondant à un certain critère (ici au fait d'être égal au caractère donné).


[Exemple] Comment manipuler un nom de fichier avec string ?
auteur : Aurélien Regat-Barrel
Le code suivant illustre des opérations courantes effectuées sur un nom de fichier :

#include <string>
#include <iostream>

int main()
{
    using namespace std;

    string file_name = "fichier.txt";

    size_t ext_pos = file_name.find_last_of( '.' );
    if ( ext_pos != string::npos )
    {
        // 3 manières d'extraire l'entension
        string ext1 = file_name.substr( ext_pos );
        string ext2( file_name, ext_pos );
        string ext3;
        ext3.assign( file_name, ext_pos, file_name.size() - ext_pos );

        // 3 manières d'extraire le nom
        string name1 = file_name.substr( 0, ext_pos );
        string name2( file_name, 0, ext_pos );
        string name3 = file_name;
        name3.resize( ext_pos );

        // remplacer le nom
        string file_name2 = file_name; // copie pour préserver l'original
        file_name2.replace( 0, ext_pos, "remplace" );

        // remplacer l'extension
        string file_name3 = file_name; // copie pour préserver l'original
        file_name3.replace( ext_pos + 1, file_name3.size() - 1, "dat" );

        // insérer un mot entre le nom du fichier et son extension
        string file_name4 = file_name; // copie pour préserver l'original
        file_name4.insert( ext_pos, "-modif" );

        // afficher les résultats
        cout << ext1 << '\n'; // ".txt"
        cout << ext2 << '\n'; // ".txt"
        cout << ext3 << '\n'; // ".txt"
        cout << name1 << '\n'; // "fichier"
        cout << name2 << '\n'; // "fichier"
        cout << name3 << '\n'; // "fichier"
        cout << file_name2 << '\n'; // "remplace.txt"
        cout << file_name3 << '\n'; // "fichier.dat"
        cout << file_name4 << '\n'; // "fichier-modif.txt"
    }
}

[Exemple] Comment manipuler un tableau de string ?
auteur : Aurélien Regat-Barrel

#include <vector>
#include <sstream>
#include <iostream>
#include <iterator>

int main()
{
    using namespace std;

    string str = "mot1 mot2 mot3 mot4 mot5 mot6";

    vector<string> str_list; // liste de mots

    // remplir la liste de mots
    istringstream iss( str );
    copy(
        istream_iterator<string>( iss ),
        istream_iterator<string>(),
        back_inserter( str_list ) );

    // afficher la liste de mots sur cout
    copy(
        str_list.begin(),
        str_list.end(),
        ostream_iterator<string>( cout, "\n" ) );

    // reconstituer une string à partir de la liste de mots
    ostringstream oss;
    copy(
        str_list.begin(),
        str_list.end(),
        ostream_iterator<string>( oss, " " ) );

    string s = oss.str();
    cout << s << '\n'; // "mot1 mot2 mot3 mot4 mot5 mot6"
}
lien : faq Comment utiliser les itérateurs de flux ?

rechercher
precedent    sommaire    suivant    telechargermiroir

Consultez les autres F.A.Q's


Valid XHTML 1.1!Valid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.