Замініть підрядок іншим підрядком C ++


91

Як я міг замінити підрядок у рядку іншим підрядком у C ++, які функції я міг використовувати?

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

5
Практично дублікат stackoverflow.com/questions/3418231/…, який має більш надійне рішення у прийнятій відповіді.
dave-holm

Відповіді:


77

У C ++ немає жодної вбудованої функції для цього. Якщо ви хочете замінити всі екземпляри одного підрядка іншим, ви можете зробити це, змішавши виклики до string::findта string::replace. Наприклад:

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

В останньому рядку цього коду я збільшив indexна довжину рядка, який був вставлений у рядок. У цьому конкретному прикладі - заміни "abc"на "def"- насправді це не потрібно. Однак у більш загальних умовах важливо пропустити рядок, який щойно був замінений. Наприклад, якщо ви хочете замінити "abc"на "abcabc", не пропускаючи нещодавно замінений сегмент рядка, цей код буде постійно замінювати частини нещодавно замінених рядків, поки пам'ять не буде вичерпана. Незалежно, все одно може бути трохи швидше пропустити ці нові символи, оскільки це економить час та зусилля string::findфункції.

Сподіваюся, це допомагає!


6
Я не вірю, що вам потрібно буде збільшувати індекс, оскільки ви вже замінили дані, щоб вони все одно не збирали їх.
rossb83

1
@Aidiakapi Якщо цю функцію перетворити на функцію загального призначення, вона не застряє у нескінченному циклі, оскільки вона висуває позицію пошуку ( index) за ту частину рядка, яка була замінена.
Тім Р.

1
@TimR. Ви маєте рацію, я відповідав rossb83, який стверджував, що збільшення індексу не потрібно. Просто намагався запобігти дезінформації. Тож для всіх інших: Збільшення індексу на довжину заміненого рядка (у цьому випадку 3) необхідно . Не виймайте його із зразка коду.
Айдіякапі

@FrozenKiwi Я здивований, почувши це. Ви впевнені, що це так?
templatetypedef

1
@JulianCienfuegos Я щойно оновив відповідь для вирішення цього питання - дякую, що вказали на це! (Крім того, Ейдіакапі - це хтось інший ... не впевнений, хто це.)
templatetypedef

68

Шлях підсилення рядкових алгоритмів :

#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"
  );
}

4
Джей. Мені потрібен boost для заміни всіх підрядків.
Йоганнес Оверманн,

2
Посилення в основному є надмірним.
Конрад


43

Я думаю, що всі рішення не вдадуться, якщо довжина рядка, що замінює, відрізняється від довжини рядка, який потрібно замінити. (шукати "abc" і замінити на "xxxxxx") Загальним підходом може бути:

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

Де

  • str є базовим рядком
  • str2 є підрядком для пошуку
  • str3 є заміною підрядка

3
Це замінює лише перше виникнення, чи не так?
jpo38

4
Я б запропонував забезпечити, щоб результат str.find (str2) не дорівнював std :: string :: npos auto found = str.find (str2); if (знайдено! = std :: string :: npos) str.replace (знайдено, str2.length (), str3);
Джефф Ленч

1
Я не мав наміру писати цілу програму з цим, але без жодної перевірки вводу є випадки цього, які не визначені ....
Джефф Захер,

19

Заміна підрядків не повинна бути такою важкою.

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

Якщо вам потрібна продуктивність, ось оптимізована функція, яка модифікує вхідний рядок, вона не створює копію рядка:

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

Тести:

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;

Вихід:

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

потрібно додати перевірку, if (search.empty()) { return; }щоб уникнути нескінченного циклу при проходженні порожнього "пошуку".
iOS-програміст

Спробував функцію ReplaceString - не спрацював. Але відповідь нижче: str.replace (str.find (str2), str2.length (), str3); просто і добре працює.
КАМІКАЗЕ

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

Оскільки він не є членом stringкласу, він не дозволяє настільки симпатичний синтаксис, як у вашому прикладі, але наступне зробить еквівалент:

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

3

Узагальнюючи відповідь rotmax, ось повне рішення для пошуку та заміни всіх екземплярів у рядку. Якщо обидва підрядки мають різний розмір, підрядок замінюється за допомогою string :: erase та string :: insert., Інакше використовується швидший рядок :: 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

Якщо ви впевнені, що необхідний підрядок присутній у рядку, це замінить перше виникнення "abc"до"hij"

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

Він вийде з ладу, якщо у вас немає "abc" у тесті, тому використовуйте його обережно.


1

Ось рішення, яке я написав, використовуючи тактику будівельника:

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

Тести:

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

Я думаю, що це відповідає вашим вимогам з кількома кодами!


Ви не берете до уваги багаторазові випадки / заміни
Еліас Бачаалані

0

збіднена версія @Czarek Tomczak.
дозволити обидва std::stringі 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

Ось рішення з використанням рекурсії, яке замінює всі випадки підрядка іншим підрядком. Це працює незалежно від розміру рядків.

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

Приклад використання:

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

Коротке рішення, де вам не потрібні додаткові бібліотеки.


На це питання є ще 14 відповідей. Чому б не запропонувати пояснення, чому твій краще?
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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.