OVH Cloud OVH Cloud

[noob] string et remplacement de chaînes

37 réponses
Avatar
TSalm
Bonjour,

Je veux remplacer , dans un string, une chaîne par une autre. Je
recherche la solution la plus simple et la plus claire (pas
spécialement la plus optimisée).

Pour l'instant je fais comme ceci :
------------------------------------------------------------
int pos = str.length();

while ((pos = str.rfind(from,pos)) != string::npos) {
str.replace( pos // position
,from.length() // taille
,to // Chaine de remplacement
);
}
------------------------------------------------------------


Voyez-vous une façon plus simple et plus courte de faire ?
(c'est pour montrer à mon CP que C++ est un language simple et
performant ;-) )

10 réponses

1 2 3 4
Avatar
David Fleury
Bonjour,

Je veux remplacer , dans un string, une chaîne par une autre. Je
recherche la solution la plus simple et la plus claire (pas
spécialement la plus optimisée).

Pour l'instant je fais comme ceci :
------------------------------------------------------------
int pos = str.length();

while ((pos = str.rfind(from,pos)) != string::npos) {
str.replace( pos // position
,from.length() // taille
,to // Chaine de remplacement
);
}
------------------------------------------------------------


Voyez-vous une façon plus simple et plus courte de faire ?
(c'est pour montrer à mon CP que C++ est un language simple et
performant ;-) )




Pourquoi rfind au lieu de find ?

David

Avatar
TSalm
Le Wed, 20 Jun 2007 23:04:37 +0200, David Fleury

------------------------------------------------------------
int pos = str.length();

while ((pos = str.rfind(from,pos)) != string::npos) {
str.replace( pos // position
,from.length() // taille
,to // Chaine de remplacement
);
}
------------------------------------------------------------



Pourquoi rfind au lieu de find ?



Pour éviter des problémes de décalage.

Si par exemple je veux remplacer "BO" par "BOBO"
dans la chaine "j'ai un BO" .
Il y a des chances qu'il me fasse une boucle infini parce qu'il va
chercher et par consequent remplacer sur la chaîne que j'ai inséré.
(à moins de rajouter un pos=pos+to.length() ...)

Si je cherche et remplace en arriére, je ne prends pas en
considération les endroits modifiés...

Une meilleurs solution ?


Avatar
David Fleury
Le Wed, 20 Jun 2007 23:04:37 +0200, David Fleury

------------------------------------------------------------
int pos = str.length();

while ((pos = str.rfind(from,pos)) != string::npos) {
str.replace( pos // position
,from.length() // taille
,to // Chaine de remplacement
);
}
------------------------------------------------------------


Pourquoi rfind au lieu de find ?



Pour éviter des problémes de décalage.

Si par exemple je veux remplacer "BO" par "BOBO"
dans la chaine "j'ai un BO" .
Il y a des chances qu'il me fasse une boucle infini parce qu'il va
chercher et par consequent remplacer sur la chaîne que j'ai inséré.
(à moins de rajouter un pos=pos+to.length() ...)

Si je cherche et remplace en arriére, je ne prends pas en
considération les endroits modifiés...


J'entends bien mais je me demandais si find ( + le décalage )
n'était pas "plus clair" pour un exemple de ce type.
l'utilisation de rfind me semblait juste une version déjà "compliqué"
dans le sens où elle cache la boucle infinie possible avec find.



Une meilleurs solution ?


Non non. voici juste la version find (qui me semble plus naturellle)

for( string::size_type pos = 0;
( pos = s.find( from, pos ) ) != string::npos;
pos += to.size() )
{
s.replace( pos, from.size(), to );
}


David



Avatar
Michael DOUBEZ
On Jun 20, 1:49 pm, Michael DOUBEZ wrote:
TSalm a écrit :> Merci à tous.
Je n'avais pas planifié aller jusqu'aux regexp.... mais je me garde
les regexp de Boost de côté.
C'est vraiment une bibliothéque très puissante.


Si c'est juste pour faire un replace, les regexp paraissent un peu
exagérées.


Qui peut le plus, peut le moins. Combien de lignes de code
est-ce qu'il t'a fallu ? Avec boost::regex, une ou deux suffit.


Une fois la fonction générique écrite, la différence n'est pas flagrante:

string str,tostr,fromstr; //parameters
string result; //resulting string

//replace fromstr by tostr from str into result
replace_range_copy(str.begin() ,str.end() ,back_inserter(result),
fromstr.begin(),fromstr.end(),
tostr.begin() ,tostr.end() );

//avec regex
//en priant que fromstr n'ai pas de caractères spéciaux
boost::regex e(string("(")+fromstr+")");
result=boost::regex_replace(str,e,tostr,
boost::match_default | boost::format_all);

Et à condition d'avoir Boost.Regex installé et compilé (puisque regex
fait partie des librairies boost à compiler).

Michael



Avatar
Michael DOUBEZ
Merci à tous.
Je n'avais pas planifié aller jusqu'aux regexp.... mais je me garde
les regexp de Boost de côté.
C'est vraiment une bibliothéque très puissante.


[snip]
Ensuite tu l'utilises sur tes string

//resulting string
string result;
//replace fromstr by tostr from str into result
replace_range_copy(str.begin() ,str.end() ,back_inserter(result),
fromstr.begin(),fromstr.end(),
tostr.begin() ,tostr.end() );


ouïe, si ça ne donne pas un code clair, ça laisse au moins présumé de
l'esprit tordus de son auteur ;-)



Comment ça pas un code clair ?
C'est sûr qu'en se limitant aux string, on peut gagner en clareté mais
là c'est générique (fonctionne sur basic_strin, vector, list et dequeue
en tous cas).

Enfin ce n'est qu'une proposition :)

Michael



Avatar
Michael DOUBEZ
C'est marrant, le code que tu as écrit fait partie des choses de la
librairie standard que je n'utilise jamais parce que je trouve que c'est
complètement illisible (quant bien même ce serait concis).

Personnellement, j'ai ma propore classe string, et il me suffit de faire:

text.replace( "old", "new" ).

C'est concis, clair, lisible.


Typiquement, ce genre de fonction ne fait pas partie des fonctions
membre. Rien n'empèche d'avoir
string& replace(string& str, string& strfrom, string& strto);

Quoique en regardant la taille de l'interface de std::string, on puisse
avoir des doutes:)

Michael

Avatar
Sylvain
Michael DOUBEZ wrote on 21/06/2007 10:45:

text.replace( "old", "new" ).


ce genre de fonction ne fait pas partie des fonctions membre.


ne fait pas partie de l'implémentation de std::string ou ne devrait pas
être une fonction membre ?

ce qui ne gênerait (en fait, des fois oui, des fois pas) serait que
cette méthode soit mutable; donc un

string replace(string& const search, string& const pattern) const;

me parait bien.

Rien n'empèche d'avoir
string& replace(string& str, string& strfrom, string& strto);


... comme fonction amie.
certes, mais transmettre la chaîne opérande est (supportablement mais
inutilement) lourd, se rappeler l'ordre de 2 params est déjà dur, alors
3 ! ;)

Sylvain.


Avatar
Loïc Joly
Bonjour,

Je veux remplacer , dans un string, une chaîne par une autre. Je
recherche la solution la plus simple et la plus claire (pas
spécialement la plus optimisée).

Pour l'instant je fais comme ceci :
------------------------------------------------------------
int pos = str.length();

while ((pos = str.rfind(from,pos)) != string::npos) {
str.replace( pos // position
,from.length() // taille
,to // Chaine de remplacement
);
}
------------------------------------------------------------


Voyez-vous une façon plus simple et plus courte de faire ?


Comme on tas déjà proposé boost, il y a aussi boost::string_algo qui
possède un certain nombre de choses assez simples d'emploi (plus que les
regex en tout cas, du moin pour qui n'a pas l'habitude de les utiliser
tous les jours). En particulier, dans ton cas :

replace_all(str, from, to);

--
Loïc

Avatar
Michael DOUBEZ
Michael DOUBEZ wrote on 21/06/2007 10:45:

text.replace( "old", "new" ).


ce genre de fonction ne fait pas partie des fonctions membre.


ne fait pas partie de l'implémentation de std::string ou ne devrait pas
être une fonction membre ?


Ne devrait pas être une fonction membre. L'implémentation d'un
search/replace ne dépend pas à priori de l'implémentation sousjacente
alors je ne vois pas l'interet d'encombrer l'interface.


ce qui ne gênerait (en fait, des fois oui, des fois pas) serait que
cette méthode soit mutable; donc un

string replace(string& const search, string& const pattern) const;

me parait bien.

Rien n'empèche d'avoir
string& replace(string& str, string& strfrom, string& strto);


... comme fonction amie.


Même pas. basic_string<> fournit tous les accesseurs utiles a un
search/replace.

certes, mais transmettre la chaîne opérande est (supportablement mais
inutilement) lourd, se rappeler l'ordre de 2 params est déjà dur, alors
3 ! ;)


Si il n'y a que ça pour faire plaisir :):

string& replace(const string& str, const pair<const string,const
string>& fromto);

Michael



Avatar
Sylvain
Michael DOUBEZ wrote on 22/06/2007 08:57:
Michael DOUBEZ wrote on 21/06/2007 10:45:

text.replace( "old", "new" ).


ce genre de fonction ne fait pas partie des fonctions membre.


ne fait pas partie de l'implémentation de std::string ou ne devrait
pas être une fonction membre ?


Ne devrait pas être une fonction membre. L'implémentation d'un
search/replace ne dépend pas à priori de l'implémentation sousjacente
alors je ne vois pas l'interet d'encombrer l'interface.


hmmm, vision différente alors, car pour moi l'implémentation concrète
d'une manipulation des données d'une instance dépends bien de ces
données, de cette instance ... si la chaîne est par exemple la
concaténation de différents segments chainés et partagés, la classe
seule (puisque tu ne veux pas même de fct amie) pourra mettre à jour sa
liste de segments.

je préférerais donc ne pas encombrer le namespace global (que je préfère
le plus vide possible).


Rien n'empèche d'avoir
string& replace(string& str, string& strfrom, string& strto);


... comme fonction amie.


Même pas. basic_string<> fournit tous les accesseurs utiles a un
search/replace.

certes, mais transmettre la chaîne opérande est (supportablement mais
inutilement) lourd, se rappeler l'ordre de 2 params est déjà dur,
alors 3 ! ;)


Si il n'y a que ça pour faire plaisir :):

string& replace(const string& str, const pair<const string,const
string>& fromto);


je lis ça comme une fonction recevant une /const string&/ et me
retournant celle-ci modifiée dans un /string&/ c'est précisément les
confusions qu'il ne me paraissait pas utile d'introduire.

Sylvain.




1 2 3 4