Zastąp podciąg innym podciągiem C ++


91

Jak mogę zamienić podciąg w ciągu na inny podciąg w C ++, jakich funkcji mogę użyć?

eg: string test = "abc def abc def";
test.replace("abc", "hij").replace("def", "klm"); //replace occurrence of abc and def with other substring

5
Prawie duplikat stackoverflow.com/questions/3418231/ ... który ma solidniejsze rozwiązanie w przyjętej odpowiedzi.
dave-holm

Odpowiedzi:


77

W C ++ nie ma jednej wbudowanej funkcji, która mogłaby to zrobić. Jeśli chcesz zamienić wszystkie wystąpienia jednego podciągu na inny, możesz to zrobić, mieszając wywołania string::findi string::replace. Na przykład:

size_t index = 0;
while (true) {
     /* Locate the substring to replace. */
     index = str.find("abc", index);
     if (index == std::string::npos) break;

     /* Make the replacement. */
     str.replace(index, 3, "def");

     /* Advance index forward so the next iteration doesn't pick it up as well. */
     index += 3;
}

W ostatnim wierszu tego kodu zwiększyłem indexo długość ciągu, który został wstawiony do ciągu. W tym konkretnym przykładzie - zastępującego "abc"z "def"- nie jest to rzeczywiście konieczne. Jednak w bardziej ogólnym ustawieniu ważne jest, aby pominąć ciąg, który właśnie został zastąpiony. Na przykład, jeśli chcesz zamienić "abc"z "abcabc", bez pomijania nad nowo wymieniony odcinek ciąg, kod ten będzie stale wymieniać części nowo zastąpionych strun dopóki pamięć została wyczerpana. Niezależnie od tego i tak pomijanie nowych znaków może być nieco szybsze, ponieważ oszczędza to trochę czasu i wysiłku string::findfunkcji.

Mam nadzieję że to pomoże!


6
Nie sądzę, abyś musiał zwiększać indeks, ponieważ już zastąpiłeś dane, aby i tak go nie odebrać.
Rossb83

1
@Aidiakapi Jeśli zostanie to przekształcone w funkcję ogólnego przeznaczenia, nie utknie w nieskończonej pętli, ponieważ przesuwa pozycję wyszukiwania ( index) poza część ciągu, który został zastąpiony.
Tim R.

1
@TimR. Masz rację, odpowiadałem rossb83, który stwierdził, że przyrost indeksu jest niepotrzebny. Próbowałem tylko zapobiec dezinformacji. Więc dla wszystkich innych: Wzrost indeksu o długości zastąpiony ciągiem (w tym przypadku 3) jest to konieczne . Nie usuwaj go z przykładowego kodu.
Aidiakapi

@FrozenKiwi Jestem zaskoczony, że to słyszę. Czy na pewno o to chodzi?
templatetypedef

1
@JulianCienfuegos Właśnie zaktualizowałem odpowiedź, aby rozwiązać ten problem - dzięki za wskazanie tego! (Również Aidiakapi to ktoś inny ... nie jestem pewien, kto to jest.)
templatetypedef

68

Sposób biblioteki algorytmów Boost String :

#include <boost/algorithm/string/replace.hpp>

{ // 1. 
  string test = "abc def abc def";
  boost::replace_all(test, "abc", "hij");
  boost::replace_all(test, "def", "klm");
}


{ // 2.
  string test = boost::replace_all_copy
  (  boost::replace_all_copy<string>("abc def abc def", "abc", "hij")
  ,  "def"
  ,  "klm"
  );
}

5
Sójka. Potrzebuję wzmocnienia, aby wymienić wszystkie podciągi.
Johannes Overmann

2
Wzmocnienie to przeważnie przesada.
Konrad


43

Myślę, że wszystkie rozwiązania zawiodą, jeśli długość zastępującego ciągu różni się od długości łańcucha do wymiany. (wyszukaj „abc” i zamień na „xxxxxx”) Ogólne podejście może być następujące:

void replaceAll( string &s, const string &search, const string &replace ) {
    for( size_t pos = 0; ; pos += replace.length() ) {
        // Locate the substring to replace
        pos = s.find( search, pos );
        if( pos == string::npos ) break;
        // Replace by erasing and inserting
        s.erase( pos, search.length() );
        s.insert( pos, replace );
    }
}

41
str.replace(str.find(str2),str2.length(),str3);

Gdzie

  • str jest ciągiem podstawowym
  • str2 to podciąg do znalezienia
  • str3 jest podciągiem zastępczym

3
To zastępuje tylko pierwsze wystąpienie, prawda?
jpo38

4
Sugerowałbym upewnienie się, że wynik str.find (str2) nie jest równy std :: string :: npos auto found = str.find (str2); if (znaleziono! = std :: string :: npos) str.replace (znalezione, str2.length (), str3);
Geoff Lentsch

1
I nie zamierza napisać całą aplikację z tego, ale bez żadnej kontroli na wejściu istnieją przypadki to, że są niezdefiniowane ....
Jeff Zacher

19

Wymiana podciągów nie powinna być taka trudna.

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;
}

Jeśli potrzebujesz wydajności, oto zoptymalizowana funkcja, która modyfikuje ciąg wejściowy, ale nie tworzy kopii ciągu:

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();
    }
}

Testy:

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;

Wynik:

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

trzeba dodać czek, if (search.empty()) { return; }aby uniknąć nieskończonej pętli, gdy wpisz puste „wyszukiwanie”.
Programista iOS

Wypróbowana funkcja ReplaceString - nie działała. Ale odpowiedz poniżej: str.replace (str.find (str2), str2.length (), str3); po prostu proste i działa dobrze.
KAMIKAZE

5
using std::string;

string string_replace( string src, string const& target, string const& repl)
{
    // handle error situations/trivial cases

    if (target.length() == 0) {
        // searching for a match to the empty string will result in 
        //  an infinite loop
        //  it might make sense to throw an exception for this case
        return src;
    }

    if (src.length() == 0) {
        return src;  // nothing to match against
    }

    size_t idx = 0;

    for (;;) {
        idx = src.find( target, idx);
        if (idx == string::npos)  break;

        src.replace( idx, target.length(), repl);
        idx += repl.length();
    }

    return src;
}

Ponieważ nie jest członkiem tej stringklasy, nie pozwala na tak ładną składnię, jak w twoim przykładzie, ale poniższy odpowiednik:

test = string_replace( string_replace( test, "abc", "hij"), "def", "klm")

3

Uogólniając na odpowiedź rotmax, oto pełne rozwiązanie do wyszukiwania i zastępowania wszystkich instancji w ciągu. Jeśli oba podciągi mają różne rozmiary, podciąg jest zamieniany za pomocą string :: erase i string :: insert., W przeciwnym razie używany jest szybszy string :: replace.

void FindReplace(string& line, string& oldString, string& newString) {
  const size_t oldSize = oldString.length();

  // do nothing if line is shorter than the string to find
  if( oldSize > line.length() ) return;

  const size_t newSize = newString.length();
  for( size_t pos = 0; ; pos += newSize ) {
    // Locate the substring to replace
    pos = line.find( oldString, pos );
    if( pos == string::npos ) return;
    if( oldSize == newSize ) {
      // if they're same size, use std::string::replace
      line.replace( pos, oldSize, newString );
    } else {
      // if not same size, replace by erasing and inserting
      line.erase( pos, oldSize );
      line.insert( pos, newString );
    }
  }
}

2

Jeśli masz pewność, że wymagany podciąg znajduje się w ciągu, zastąpi to pierwsze wystąpienie "abc"to"hij"

test.replace( test.find("abc"), 3, "hij");

Jeśli nie masz w teście „abc”, nastąpi awaria, więc używaj go ostrożnie.


1

Oto rozwiązanie, które napisałem przy użyciu taktyki konstruktora:

#include <string>
#include <sstream>

using std::string;
using std::stringstream;

string stringReplace (const string& source,
                      const string& toReplace,
                      const string& replaceWith)
{
  size_t pos = 0;
  size_t cursor = 0;
  int repLen = toReplace.length();
  stringstream builder;

  do
  {
    pos = source.find(toReplace, cursor);

    if (string::npos != pos)
    {
        //copy up to the match, then append the replacement
        builder << source.substr(cursor, pos - cursor);
        builder << replaceWith;

        // skip past the match 
        cursor = pos + repLen;
    }
  } 
  while (string::npos != pos);

  //copy the remainder
  builder << source.substr(cursor);

  return (builder.str());
}

Testy:

void addTestResult (const string&& testId, bool pass)
{
  ...
}

void testStringReplace()
{
    string source = "123456789012345678901234567890";
    string toReplace = "567";
    string replaceWith = "abcd";
    string result = stringReplace (source, toReplace, replaceWith);
    string expected = "1234abcd8901234abcd8901234abcd890";

    bool pass = (0 == result.compare(expected));
    addTestResult("567", pass);


    source = "123456789012345678901234567890";
    toReplace = "123";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "-4567890-4567890-4567890";

    pass = (0 == result.compare(expected));
    addTestResult("start", pass);


    source = "123456789012345678901234567890";
    toReplace = "0";
    replaceWith = "";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "123456789123456789123456789"; 

    pass = (0 == result.compare(expected));
    addTestResult("end", pass);


    source = "123123456789012345678901234567890";
    toReplace = "123";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "--4567890-4567890-4567890";

    pass = (0 == result.compare(expected));
    addTestResult("concat", pass);


    source = "1232323323123456789012345678901234567890";
    toReplace = "323";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "12-23-123456789012345678901234567890";

    pass = (0 == result.compare(expected));
    addTestResult("interleaved", pass);



    source = "1232323323123456789012345678901234567890";
    toReplace = "===";
    replaceWith = "-";
    result = utils_stringReplace(source, toReplace, replaceWith);
    expected = source;

    pass = (0 == result.compare(expected));
    addTestResult("no match", pass);

}

0
    string & replace(string & subj, string old, string neu)
    {
        size_t uiui = subj.find(old);
        if (uiui != string::npos)
        {
           subj.erase(uiui, old.size());
           subj.insert(uiui, neu);
        }
        return subj;
    }

Myślę, że to pasuje do twoich wymagań z kilkoma kodami!


Nie bierzesz pod uwagę wielokrotnych wystąpień / zamiany
Elias Bachaalany

0

ulepszona wersja autorstwa @Czarek Tomczak.
zezwalaj na std::stringi std::wstring.

template <typename charType>
void ReplaceSubstring(std::basic_string<charType>& subject,
    const std::basic_string<charType>& search,
    const std::basic_string<charType>& replace)
{
    if (search.empty()) { return; }
    typename std::basic_string<charType>::size_type pos = 0;
    while((pos = subject.find(search, pos)) != std::basic_string<charType>::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

0
std::string replace(const std::string & in
                  , const std::string & from
                  , const std::string & to){
  if(from.size() == 0 ) return in;
  std::string out = "";
  std::string tmp = "";
  for(int i = 0, ii = -1; i < in.size(); ++i) {
    // change ii
    if     ( ii <  0 &&  from[0] == in[i] )  {
      ii  = 0;
      tmp = from[0]; 
    } else if( ii >= 0 && ii < from.size()-1 )  {
      ii ++ ;
      tmp = tmp + in[i];
      if(from[ii] == in[i]) {
      } else {
        out = out + tmp;
        tmp = "";
        ii = -1;
      }
    } else {
      out = out + in[i];
    }
    if( tmp == from ) {
      out = out + to;
      tmp = "";
      ii = -1;
    }
  }
  return out;
};

0

Oto rozwiązanie wykorzystujące rekursję, która zastępuje wszystkie wystąpienia podciągu innym. Działa to niezależnie od rozmiaru strun.

std::string ReplaceString(const std::string source_string, const std::string old_substring, const std::string new_substring)
{
    // Can't replace nothing.
    if (old_substring.empty())
        return source_string;

    // Find the first occurrence of the substring we want to replace.
    size_t substring_position = source_string.find(old_substring);

    // If not found, there is nothing to replace.
    if (substring_position == std::string::npos)
        return source_string;

    // Return the part of the source string until the first occurance of the old substring + the new replacement substring + the result of the same function on the remainder.
    return source_string.substr(0,substring_position) + new_substring + ReplaceString(source_string.substr(substring_position + old_substring.length(),source_string.length() - (substring_position + old_substring.length())), old_substring, new_substring);
}

Przykład użycia:

std::string my_cpp_string = "This string is unmodified. You heard me right, it's unmodified.";
std::cout << "The original C++ string is:\n" << my_cpp_string << std::endl;
my_cpp_string = ReplaceString(my_cpp_string, "unmodified", "modified");
std::cout << "The final C++ string is:\n" << my_cpp_string << std::endl;

0
std::string replace(std::string str, std::string substr1, std::string substr2)
{
    for (size_t index = str.find(substr1, 0); index != std::string::npos && substr1.length(); index = str.find(substr1, index + substr2.length() ) )
        str.replace(index, substr1.length(), substr2);
    return str;
}

Krótkie rozwiązanie, w którym nie potrzebujesz żadnych dodatkowych bibliotek.


Istnieje 14 innych odpowiedzi na to pytanie. Dlaczego nie wyjaśnić, dlaczego twój jest lepszy?
chb

0
std::string replace(std::string str, const std::string& sub1, const std::string& sub2)
{
    if (sub1.empty())
        return str;

    std::size_t pos;
    while ((pos = str.find(sub1)) != std::string::npos)
        str.replace(pos, sub1.size(), sub2);

    return str;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.