Заменить подстроку другой подстрокой 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функции.

Надеюсь это поможет!

templatetypedef
источник
6
Я не верю, что вам нужно будет увеличивать индекс, потому что вы уже заменили данные, поэтому он все равно их не заберет.
rossb83 04
1
@Aidiakapi Если его превратить в функцию общего назначения, он не застрянет в бесконечном цикле, потому что он продвигает позицию поиска ( index) за часть замененной части строки.
Тим Р.
1
@TimR. Вы правы, я отвечал rossb83, который заявил, что в увеличении индекса нет необходимости. Просто пытался предотвратить дезинформацию. Так что для всех остальных: Увеличение индекса по длине заменяемой строки (в данном случае 3) необходимо . Не удаляйте его из образца кода.
Aidiakapi
@FrozenKiwi Я удивлен это слышать. Вы уверены, что это так?
templatetypedef
1
@JulianCienfuegos Я только что обновил ответ, чтобы решить эту проблему - спасибо, что указали на это! (Кроме того, Айдиакапи - это кто-то другой ... не уверен, кто это.)
templatetypedef
68

Библиотека алгоритмов 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
Джей. Мне нужен форс, чтобы заменить все подстроки.
Йоханнес Оверманн
2
Boost - это вообще перебор.
Конрад
63

В , вы можете использовать std::regex_replace:

#include <string>
#include <regex>

std::string test = "abc def abc def";
test = std::regex_replace(test, std::regex("def"), "klm");
Цзинго Яо
источник
4
Было бы здорово, если бы у нас был c ++ 11 !!
Мишель
1
#include <regex>
Степан Яковенко
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 );
    }
}
Rotmax
источник
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); если (найдено! = std :: string :: npos) str.replace (found, 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); просто и хорошо работает.
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;
}

Поскольку он не является членом 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", поэтому используйте его с осторожностью.

ch0kee
источник
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();
    }
}
iOS-программист
источник
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;
}

Краткое решение, когда вам не нужны дополнительные библиотеки.

Altinsystems
источник
1
Есть еще 14 ответов на этот вопрос. Почему бы не объяснить, почему ваш лучше?
chb 04
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;
}
Alex
источник