web-dev-qa-db-fra.com

Remplacer une partie d'une chaîne par une autre chaîne

Est-il possible en C++ de remplacer une partie d'une chaîne par une autre chaîne?

En gros, j'aimerais faire ceci:

QString string("hello $name");
string.replace("$name", "Somename");

Mais j'aimerais utiliser les bibliothèques Standard C++.

171
Tom Leese

Il existe une fonction pour rechercher une sous-chaîne dans une chaîne ( find ), et une fonction pour remplacer une plage particulière d'une chaîne par une autre chaîne ( replace ), vous pouvez donc les combiner pour obtenir l’effet souhaité:

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

En réponse à un commentaire, je pense que replaceAll ressemblerait probablement à ceci:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}
264
Michael Mrozek

Avec C++ 11, vous pouvez utiliser std::regex comme suit:

    #include <regex>
    ...
    std::string string("hello $name");
    string = std::regex_replace(string, std::regex("\\$name"), "Somename");

La double barre oblique inversée est nécessaire pour échapper à un caractère d'échappement.

79
Tom

std::string a une méthode replace , est-ce ce que vous recherchez?

Tu pourrais essayer:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

Je n'ai pas essayé moi-même, il suffit de lire la documentation sur find() et replace().

18
S.C. Madsen

Pour que la nouvelle chaîne soit renvoyée, utilisez ceci:

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 en 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 modified: " 
          << 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
8
Czarek Tomczak

Oui, vous pouvez le faire, mais vous devez trouver la position de la première chaîne avec le membre find () de string, puis la remplacer par son membre replace ().

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

Si vous envisagez d’utiliser la bibliothèque standard, vous devriez vous procurer un exemplaire du livre The C++ Standard Library , qui couvre très bien tout cela.

6
anon

Cela ressemble à une option

string.replace(string.find("%s"), string("%s").size(), "Something");

Vous pouvez envelopper cela dans une fonction, mais cette solution sur une ligne semble acceptable. Le problème est que cela ne changera que la première occurrence, vous voudrez peut-être boucler dessus, mais cela vous permettra également d'insérer plusieurs variables dans cette chaîne avec le même jeton (%s)

4
maxoumime

J'utilise généralement ceci:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

Il appelle à plusieurs reprises std::string::find() pour localiser d'autres occurrences de la chaîne recherchée jusqu'à ce que std::string::find() ne trouve plus rien. Parce que std::string::find() renvoie le position du match, nous n'avons pas le problème d'invalider les itérateurs.

4
Galik

Si toutes les chaînes sont std :: string, vous rencontrerez d'étranges problèmes avec la coupure de caractères si vous utilisez sizeof() car il est destiné aux chaînes C, pas aux chaînes C++. Le correctif consiste à utiliser la méthode .size() class de std::string.

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

Cela remplace sHaystack inline - pas besoin de faire une assignation = dessus.

Exemple d'utilisation:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;
3
Volomike
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);
2
user3016543
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}
2
Lucas Civali

Si vous voulez le faire rapidement, vous pouvez utiliser une approche à deux numérisations. Pseudo code:

  1. première analyse. trouvez combien de caractères correspondants.
  2. augmenter la longueur de la chaîne.
  3. deuxième analyse. Commençons à la fin de la chaîne lorsque nous obtenons une correspondance que nous remplaçons, sinon nous copions simplement les caractères de la première chaîne.

Je ne suis pas sûr si cela peut être optimisé pour un algo en place.

Et un exemple de code C++ 11 mais je ne cherche qu'un seul caractère.

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}
1
Damian

Je viens juste d'apprendre le C++, mais en modifiant une partie du code précédemment publié, j'utiliserais probablement quelque chose comme ceci. Cela vous donne la possibilité de remplacer une ou plusieurs instances et vous permet également de spécifier le point de départ.

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}
0
someprogrammer

Ma propre implémentation, tenant compte du fait que la chaîne ne doit être redimensionnée qu'une seule fois, est remplacée.

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

L'utilisation pourrait être par exemple comme ceci:

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");
0
TarmoPikaro