【问题标题】:How do I Search/Find and Replace in a standard string?如何在标准字符串中搜索/查找和替换?
【发布时间】:2010-12-02 10:30:43
【问题描述】:

有没有办法用std::string中的另一个字符串替换所有出现的子字符串?

例如:

void SomeFunction(std::string& str)
{
   str = str.replace("hello", "world"); //< I'm looking for something nice like this
}

【问题讨论】:

标签: c++ replace std


【解决方案1】:
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

这里是 the official documentation 上的 replace_all。

【讨论】:

  • 请注意,您不必为模式和替换显式创建 std::string: boost::replace_all(target, "foo", "bar");
  • +1,需要注意的是:replace_all 对于任何版本 的 Sun Studio 上的 boost > 1.43 版本都会出现段错误
  • boost 在嵌入式设备上显着增加了编译时间。甚至 ARMv7 四核。 100行代码2分钟编译,不用boost,2秒。
  • @ppumkin :这意味着您的编译器(或构建设置或其他)很糟糕,而不是目标架构,这与它无关。
  • 如果您的编译器支持预编译头文件,强烈建议在使用 boost 时使用它。真的很省时间。
【解决方案2】:

为什么不实现自己的替换?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

【讨论】:

  • 您在这里对所有对“replace”的调用都弄乱了内存:如果从“ooooooo...o”中删除“o”,复杂性将是n²。我想可以做得更好,但这种解决方案的优点是易于理解。
  • 为什么这不是一个真正的 for 循环,而是一个混淆的 for 循环?
  • 我习惯于应用“最小意外”原则。大多数时候,for 循环用于简单的索引增量。在这里,根据我的说法,while 循环更清晰。
  • @aldo 作为一般规则,最好避免复杂性,例如,使用其他回复中提到的正则表达式。但是根据您的需要,您可能希望控制您的项目依赖项。一个小代码 sn-p 可以完全满足您的需要,仅此而已,有时会更好。
  • 这是O(n^2)。对于O(n),应避免重复使用replace。例如,请参阅this implementation
【解决方案3】:

在 C++11 中,您可以通过调用regex_replace 来实现这一点:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

输出:

Removeallspaces

【讨论】:

  • 谢谢,非常容易使用和记住!
  • 还要注意from 可以是一个正则表达式——因此如果需要,您可以使用更复杂的匹配条件。我没有看到,如何做到这一点应用某种形式的正则表达式解析——而不是只使用from字符的直接解释。
  • 这可能需要最新的编译器。它适用于 gcc 5.0,但我在使用 gcc 4.8.4 时遇到了一些问题。
  • 请注意,这仅适用于非常基本的字母数字字符,而无需根据字符串类型进行大量预处理。我还没有找到一个通用的基于正则表达式的字符串替换。
  • 我认为有必要注意它可能会比简单的字符串操作(如boost::replace_all)慢得多。
【解决方案4】:

为什么不返回修改后的字符串?

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

【讨论】:

  • 如果要替换多个字符串,您的就地版本实际上会做不必要的工作。想象一下用短字符串替换长字符串。替换将不得不在每个找到的条目上移动字符串的其余部分。这对缓存不利且不必要。
【解决方案5】:

我的模板化内联就地查找和替换:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

它返回被替换的项目数的计数(如果你想连续运行它等使用)。要使用它:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

【讨论】:

  • 我在 GCC 下尝试了这个示例,但它无法编译 - 它不喜欢使用 T::size_t。用 typename T::size_type 替换 T::size_t 可以解决问题。
【解决方案6】:

最简单的方法(提供与您所写内容相近的内容)是使用Boost.Regex,特别是regex_replace

std::string 内置了 find() 和 replace() 方法,但它们使用起来更麻烦,因为它们需要处理索引和字符串长度。

【讨论】:

  • 还有boost字符串算法,包括replace_all(对于这种简单的替换,正则表达式可能有点重)。
【解决方案7】:

我相信这会奏效。 它以 const char*'s 作为参数。

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

【讨论】:

  • 假设字符串的size_typeunsigned,您的&gt;= 循环条件检查将始终为true。你必须在那里使用std::string::npos
  • size_type 不是无符号的。它在许多平台上都未签名,但不是全部。
  • 为什么这不是 std::string 的一部分?编程世界中是否还有其他严重的 String 类不提供“查找和替换”操作?当然,这比拥有两个迭代器并想要替换它们之间的文本更常见?有时 std::string 感觉就像一辆带有可调光谱挡风玻璃但无法摇下驾驶员车窗的汽车。
  • @Spike0xff 提升有roll_down_window
  • @gustafr:我的错误。我曾在旧编译器不正确地定义 size_t 的系统上工作过。
【解决方案8】:
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

检查 oldStr 是否为空很重要。如果由于某种原因该参数为空,您将陷入无限循环。

但是,如果可以的话,可以使用久经考验的 C++11 或 Boost 解决方案。

【讨论】:

  • 这对于修复一些调试信息非常方便,因为我只是将它粘贴到一个没有任何依赖关系的现有文件中......谢谢!
【解决方案9】:
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

【讨论】:

  • 我们只需要从最后一个匹配中搜索新的匹配,这就是为什么算法会仔细跟踪pos中的最后一个匹配。 pos2 总是存储下一个匹配项,因此我们将 pos 和 pos2 之间的字符串连接到结果,然后将 pos 和 pos2 推进。如果找不到其他匹配项,我们将字符串的其余部分连接到结果。
【解决方案10】:

性能 O(n) 全部替换

很多其他答案重复调用std::string::replace,需要重复覆盖字符串,导致性能不佳。相比之下,这使用了std::ostringstream 缓冲区,因此字符串的每个字符只被遍历一次:

void replace_all(
    std::string& s,
    std::string const& toReplace,
    std::string const& replaceWith
) {
    std::ostringstream oss;
    std::size_t pos = 0;
    std::size_t prevPos;

    while (true) {
        prevPos = pos;
        pos = s.find(toReplace, pos);
        if (pos == std::string::npos)
            break;
        oss << s.substr(prevPos, pos - prevPos);
        oss << replaceWith;
        pos += toReplace.size();
    }

    oss << s.substr(prevPos);
    s = oss.str();
}

用法:

replace_all(s, "text to replace", "new text");

完整示例:
#include <iostream>
#include <sstream>

void replace_all(
    std::string& s,
    std::string const& toReplace,
    std::string const& replaceWith
) {
    std::ostringstream oss;
    std::size_t pos = 0;
    std::size_t prevPos;

    while (true) {
        prevPos = pos;
        pos = s.find(toReplace, pos);
        if (pos == std::string::npos)
            break;
        oss << s.substr(prevPos, pos - prevPos);
        oss << replaceWith;
        pos += toReplace.size();
    }

    oss << s.substr(prevPos);
    s = oss.str();
}

int main() {
    std::string s("hello hello, mademoiselle!");
    replace_all(s, "hello", "bye");
    std::cout << s << std::endl;
}

输出:

bye bye, mademoiselle!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 2018-01-02
    相关资源
    最近更新 更多