109 votes

Comment rechercher/trouver et remplacer dans une chaîne de caractères standard ?

Existe-t-il un moyen de remplacer toutes les occurrences d'une sous-chaîne par une autre chaîne de caractères dans l'application std::string ?

Par exemple :

void SomeFunction(std::string& str)
{
   str = str.replace("hello", "world"); //< I'm looking for something nice like this
}

0 votes

171voto

ilyaipetrov Points 876
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

Voici la documentation officielle sur replace_all.

1 votes

Notez que vous n'avez pas besoin de créer explicitement des chaînes std::string pour le motif et le remplacement : boost::replace_all(target, "foo", "bar") ;

4 votes

+1, avec une mise en garde : replace_all sera en défaut pour les versions de boost > 1.43 sur Sun Studio pour toute version < 12.3

3 votes

boost augmente considérablement le temps de compilation sur les dispositifs embarqués. Même ARMv7 quad core. 100 lignes de code se compilent en 2 minutes, sans boost, 2 secondes.

81voto

yves Baumes Points 4521

Pourquoi ne pas mettre en place votre propre remplacement ?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

4 votes

Vous jouez un peu avec la mémoire ici avec tous les appels à "replace" : la complexité serait n² si vous enlevez "o" de "ooooooo...o". Je suppose qu'on peut faire mieux, mais cette solution a le mérite d'être facile à comprendre.

1 votes

Pourquoi ne s'agit-il pas d'une véritable boucle for, plutôt que d'une boucle for obscurcie ?

0 votes

J'ai l'habitude d'appliquer le principe de la "moindre surprise". Les boucles For sont destinées à une utilisation simple d'incrémentation d'index, la plupart du temps. Ici, selon moi, une boucle while est plus claire.

19voto

Czarek Tomczak Points 4551

Pourquoi ne pas renvoyer une chaîne modifiée ?

std::string ReplaceString(std::string subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

Si vous avez besoin de performances, voici une fonction optimisée qui modifie la chaîne d'entrée, elle ne crée pas de copie de la chaîne :

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

Tests :

std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;

std::cout << "ReplaceString() return value: " 
          << ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not changed: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

Sortie :

Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def

1 votes

Votre version inplace fait en fait un travail inutile s'il y a plus d'une chaîne à remplacer. Imaginez que vous remplacez une longue chaîne par une courte. Replace devra déplacer le reste de la chaîne à chaque entrée trouvée. C'est à la fois mauvais pour le cache et inutile.

6voto

Marius Points 2008

Mon modèle de recherche et de remplacement en ligne :

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

Elle retourne un compte du nombre d'éléments substitués (à utiliser si vous voulez exécuter successivement ceci, etc). Pour l'utiliser :

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

5 votes

J'ai essayé cet exemple sous GCC mais il ne compilait pas - il n'aimait pas l'utilisation de T::size_t. Remplacer T::size_t par typename T::size_type résout le problème.

3voto

Alan Points 21367

Le moyen le plus simple (offrant quelque chose de proche de ce que vous avez écrit) est d'utiliser Boost.Regex et plus particulièrement regex_replace .

std::string possède des méthodes find() et replace() intégrées, mais elles sont plus difficiles à utiliser car elles nécessitent de traiter des indices et des longueurs de chaîne.

3 votes

Il y a aussi les algorithmes boost string, dont replace_all (regex est peut-être un peu lourd pour une substitution aussi simple).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X