用另一个子串替换子串 C++

140

在C++中,我该如何使用哪些函数将字符串中的子字符串替换为另一个子字符串?

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

7
这句话的意思是:这篇文章几乎与https://dev59.com/TXA75IYBdhLWcg3wOGLS相同,而那篇文章在被选中的答案中提供了一个更健壮的解决方案。 - dave-holm
20个回答

119

中,你可以使用std::regex_replace

#include <string>
#include <regex>

std::string test = "abc def abc def";
test = std::regex_replace(test, std::regex("def"), "klm"); // replace 'def' -> 'klm'
// test = "abc klm abc klm"

4
如果我们有C++11的话,那就太好了! - Michele
5
请注意,这个不是通用的,你可能会遇到一些 std:regex 解释不同的情况,比如 std::regex_replace(test, std::regex("."), "klm") ... - Thomas Vincent
@ThomasVincent,你能否更详细地解释一下这个问题,可能需要在回答块中,而不仅仅是在评论中吗? - Amit
5
@Amit 我不知道它是否值得一个答案块。我只是想说,对于简单的“def”示例,它可以工作,但如果你想要替换的内容在正则表达式语法中有意义,那么你将无法得到预期的结果!例如,如果你想要替换“.json”,你将不得不使用std::regex("\.json"),否则(几乎)任何以“json”结尾的子字符串也将被替换。这是因为在正则表达式中,“.”表示(几乎)任何字符... - Thomas Vincent
1
@ThomasVincent 精确匹配 std::regex("\\.json") - Danvil

102

在C++中没有一个内置函数来实现这个功能。如果你想用另一个子字符串替换所有实例,可以通过交错调用string::findstring::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增加了插入到字符串中的字符串的长度。在这个特定的例子中——用"def"替换"abc"——这实际上是不必要的。然而,在一个更普遍的情况下,跳过刚刚被替换的字符串是很重要的。例如,如果你想用"abcabc"替换"abc",而不跳过新替换的字符串段,这段代码会持续替换新替换的字符串的部分,直到内存耗尽。独立地说,跳过这些新字符可能会稍微快一点,因为这样做可以节省一些时间和string::find函数的努力。

9
我认为你不需要增加索引值,因为你已经替换了数据,所以它不会再被检索到。 - rossb83
1
如果将此转换为通用函数,则不会陷入无限循环,因为它会将搜索位置(index)推进到已替换的字符串部分之后。 - Tim R.
3
@TimR. 你说得对,我在回应rossb83时,他声称索引增量是不必要的。我只是想防止误导。所以,对于其他人来说:将索引递增替换字符串的长度(在本例中为3)是必要的。请勿从代码示例中删除它。 - Aidiakapi
1
@rossb83 这些注释需要进行清理或者进一步的解释。有一个评论获得了5个赞,说你不需要增加索引,然后另一个人用粗体字说你需要增加。这对于来到这里学习的人没有帮助。 - user2918461
1
@JulianCienfuegos,我刚刚更新了答案以解决这个问题 - 感谢您指出!(另外,Aidiakapi是别人...不确定是谁。) - templatetypedef
显示剩余4条评论

79

Boost字符串算法库的方法:

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

10
杰伊,我需要升级以替换所有子字符串。 - Johannes Overmann
6
Boost 往往是过度杀伤力。 - Konrad
2
我觉得很有趣的是,对于C++问题的绝大部分答案都可以用Boost提供的简单、简洁的解决方案来回答,而且每一个答案都会附带一条"Boost太过复杂"的评论。我无法想象在没有Boost的情况下如何使用C++... - Oversearch

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

这里的情况是:

  • str 是基本字符串
  • str2 是要查找的子字符串
  • str3 是替换用的子字符串

4
这只替换第一次出现,是吗? - jpo38
10
建议确保 str.find(str2) 的结果不等于 std::string::npos。 如果找到了,则将其替换为 str3,同时使用 str2.length() 覆盖 str2 的长度。代码示例:auto found = str.find(str2); if(found != std::string::npos) str.replace(found, str2.length(), str3); - Geoff Lentsch
1
我并不打算用这个来编写整个应用程序,但是如果没有对输入进行任何检查,就会出现未定义的情况... - Jeff Zacher

50

我认为,如果替换字符串的长度与待替换的字符串的长度不同,那么所有解决方案都会失败。(搜索"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 );
    }
}

23

替换子字符串并不难。

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; } 来避免传递空的 'search' 时出现无限循环。 - iOS-programmer
尝试使用ReplaceString函数 - 没有起作用。但下面的答案很简单且有效:str.replace(str.find(str2),str2.length(),str3);。 - KAMIKAZE

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

不需要任何额外库的简短解决方案。


1
这个问题有其他14个答案。为什么不解释一下你的答案比其他人的好呢? - chb
3
目前为止,看起来这是最优雅的答案,没有过度设计。 - Ichthyo

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

4

如果您确定字符串中包含所需的子字符串,则将其替换为第一个出现的"abc",并替换为"hij"

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

如果你的测试中没有“abc”,它将崩溃,因此请谨慎使用。


4

继承rotmax的回答,下面是一个完整的方案,可用于搜索和替换字符串中的所有实例。如果两个子字符串的大小不同,则使用string::erase和string::insert替换子字符串。否则,会使用更快的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 );
    }
  }
}

谢谢。使用了几年没有问题;然而最终需要将oldStringnewString都变成const参数。 - Ron Burk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接