如何在标准字符串中搜索/查找和替换?

问题描述 投票:0回答:11

如何将所有出现的子字符串替换为另一个字符串,对于

std::string
s?

std::string s ("One hello, two hellos.");
s = s.replace("hello", "world");  // something like this
c++ replace std
11个回答
171
投票

使用

boost::replace_all

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

80
投票

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

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

45
投票

在 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

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

6
投票

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

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

4
投票

性能 O(n) 替换全部

很多其他答案重复调用

std::string::replace
,这需要重复覆盖字符串,导致性能不佳。相反,它使用
std::string
缓冲区,以便字符串的每个字符仅遍历一次:

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

    // Reserves rough estimate of final size of string.
    buf.reserve(s.size());

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

    buf.append(s, prevPos, s.size() - prevPos);
    s.swap(buf);
}

用途:

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

完整示例:

#include <iostream>

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

    // Reserves rough estimate of final size of string.
    buf.reserve(s.size());

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

    buf.append(s, prevPos, s.size() - prevPos);
    s.swap(buf);
}

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

输出:

bye bye, mademoiselle!

注意:此答案的先前版本使用

std::ostringstream
,这有一些开销。最新版本使用
std::string::append
代替,如 @LouisGo 推荐。


3
投票

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

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


3
投票

我相信这会起作用。 它采用 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; 
   }
}

3
投票
#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 解决方案。


1
投票
// 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;
}

0
投票

这是我的纯C++标准库replace_all实现,重点是尽可能获得最佳性能。必要时最多可进行 1 次重新分配。代码注释得很好。

///
/// string replace all pattern occurences with replacement
/// returns number of performed replaces
/// assert() may be replaced with any kind of error reporting
/// may be easy changed to pure C implementation
/// NOTE: result of string_replace_all("xxx", "xx", "XX") is undefined, may ends with: "XXx" or "xXX" (for #2b)
///
size_t string_replace_all(std::string& s, const char* pattern, size_t pattern_len, const char* replacewith, size_t replacewith_len) {
  if (s.empty()) return 0; // no space to search
  if (pattern_len == 0) return 0; // nothing to find

  // sanity checks:
  assert(pattern != nullptr);
  if (replacewith_len != 0) assert(replacewith != nullptr);
  
  size_t l = s.size(), founds = 0; // string size, number of performed replaces
  const char* const pattern_end = pattern + pattern_len;
  char* it2, *it = &s.front(); // ,processed string first character
  char* dst, *end = it + l; // ,end of processed string

  // I hope nobody has a such idea, but of course...
  assert(pattern_end <= it || pattern >= end); // ...pattern can't be the part of the string
  if (replacewith_len != 0)
    assert(replacewith + replacewith_len <= it || replacewith >= end); // ...replacewith can't be the part of the string


  if (replacewith_len > pattern_len) { // #1 possible growing:
    // 1st pass - compute grow size:
    while ((it = std::search(it, end, pattern, pattern_end)) != end) {
      it += pattern_len;
      ++founds;
    }

    if (founds == 0) return 0;
    size_t osz = l, nsz = l + founds * (replacewith_len - pattern_len); // old size, compute new size

    // 2nd pass:
    if (nsz > s.capacity()) { // #1a realloaction needed
      std::string s2; // it's better to allocate new string and just write from the beginging
      s2.reserve(nsz); // allocate enough memory
      it = &s.front(); // rewind 'it' (as source)

      it2 = std::search(it, end, pattern, pattern_end);
      assert(it2 != end); // first found must exists
      do {
        s2.append(it, it2 - it); // append [it, it2) unchanged
        s2.append(replacewith, replacewith_len); // append replacement in the pattern place
        it = it2 + pattern_len; // jump over found pattern
      }
      while ((it2 = std::search(it, end, pattern, pattern_end)) != end);

      s2.append(it, end - it); // extend with fragment after last found
      assert(s2.size() == nsz); // to make sure
      std::swap(s, s2); // s2 becomes s and original s will be free
    }
    else { // #1b no reallocation needed:
      s.resize(nsz); // extending size with null bytes - should not reallocate
      end = &s.front() + nsz; // expected string end after 2nd pass
      char* oend = &s.front() + osz; // old end / real current string end

      it = std::find_end(&s.front(), oend, pattern, pattern_end);
      assert(it != oend); // first found must exists
      do {
        // first move following (after pattern) fragment to the right:
        char* b = it + pattern_len; // fragment begin
        l = oend - b; // fragment length
        end -= l; // moving destination
        std::char_traits<char>::move(end, b, l); // move right: b -> end

        // then insert replacement:
        end -= replacewith_len;
        std::char_traits<char>::copy(end, replacewith, replacewith_len);

        oend = it;
      }
      while ((it = std::find_end(&s.front(), oend, pattern, pattern_end)) != oend);
    }
  }

  else if (replacewith_len < pattern_len) { // #2 possible shrinking:
    it = std::search(it, end, pattern, pattern_end);
    if (it == end) return 0;

    std::char_traits<char>::copy(it, replacewith, replacewith_len); // paste replacement on the first pattern found
    dst = it + replacewith_len; // destination of the following fragments
    it += pattern_len; // continue from pattern end
    ++founds;
    
    while ((it2 = std::search(it, end, pattern, pattern_end)) != end) {
      // first move preceding (before this and after previous) pattern to the dest:
      l = it2 - it;
      std::char_traits<char>::move(dst, it, l); // move left: dst <- it
      dst += l;

      // then insert replacement:
      std::char_traits<char>::copy(dst, replacewith, replacewith_len);
      dst += replacewith_len;

      it = it2 + pattern_len; // continue from pattern end
      ++founds;
    }

    l = end - it;
    std::char_traits<char>::move(dst, it, l); // move fragment after last pattern
    dst += l;

    s.resize(dst - &s.front()); // update string size (to lower)
  }

  else { // #3 - replacing without size affection:
    // detect: pattern == replacewith
    if (std::char_traits<char>::compare(pattern, replacewith, pattern_len) == 0) return 0;

    // perform simple replacing:
    while ((it = std::search(it, end, pattern, pattern_end)) != end) {
      std::char_traits<char>::copy(it, replacewith, replacewith_len);
      it += replacewith_len;
      ++founds;
    }
  }

  return founds;
}

// std::string arguments overload
inline size_t string_replace_all(std::string& s, const std::string& pattern, const std::string& replacewith) {
  return string_replace_all(s, pattern.data(), pattern.size(), replacewith.data(), replacewith.size());
}

// null termiated arguments overload
inline size_t string_replace_all(std::string& s, const char* pattern, const char* replacewith) {
  assert(pattern != nullptr);
  return string_replace_all(s, pattern, std::strlen(pattern), replacewith, (replacewith != nullptr) ? std::strlen(replacewith) : 0);
}

测试

void make_test(const char* str, const char* p, const char* r, const char* expect, std::string& target) {
  target = str;
  size_t c = string_replace_all(target, p, r);
  std::printf("string_replace_all('%s', '%s', '%s') => %i '%s' %s\n", str, p, r, (int)c, target.c_str(), target == expect ? "OK" : "FAIL!");
}

void make_test(const char* str, const char* p, const char* r, const char* expect) {
  std::string target;
  make_test(str, p, r, expect, target);
}

int main() {
  std::string reserved;
  reserved.reserve(128);

  make_test("abc", "x", "XX", "abc"); // #1 not found
  make_test("abc", "x", "", "abc"); // #2 not found
  make_test("abc", "x", "X", "abc"); // #3 not found
  make_test("axbcxdxxefxgxh", "x", "XXXX", "aXXXXbcXXXXdXXXXXXXXefXXXXgXXXXh"); // #1a middle
  make_test("xaxbcdxefxgxhx", "x", "XXXX", "XXXXaXXXXbcdXXXXefXXXXgXXXXhXXXX"); // #1a edges
  make_test("abxdxefgxhixj", "x", "XX", "abXXdXXefgXXhiXXj", reserved); // #1b middle
  make_test("xxabxxcdxexfx", "x", "XX", "XXXXabXXXXcdXXeXXfXX", reserved); // #1b edges
  make_test("abxxdexxxxgh", "xx", "X", "abXdeXXgh"); // #2 middle
  make_test("xxabxxxxcdxxefxx", "xx", "X", "XabXXcdXefX"); // #2 edges
  make_test("abxxxcdxexf", "x", nullptr, "abcdef"); // #2 middle remove
  make_test("xxabxxxcdxefxx", "x", nullptr, "abcdef"); // #2 edges remove
  make_test("xxxxxxxxxxxxxxx", "x", nullptr, ""); // #2 remove all
  make_test("abxxcxdexxxxf", "xx", "XX", "abXXcxdeXXXXf"); // #3 middle
  make_test("xxabxxxcxxxxx", "xx", "XX", "XXabXXxcXXXXx"); // #3 edges
  make_test("abxxcd", "x", "x", "abxxcd"); // #3 equals
}

输出:

string_replace_all('abc', 'x', 'XX') => 0 'abc' OK
string_replace_all('abc', 'x', '') => 0 'abc' OK
string_replace_all('abc', 'x', 'X') => 0 'abc' OK
string_replace_all('axbcxdxxefxgxh', 'x', 'XXXX') => 6 'aXXXXbcXXXXdXXXXXXXXefXXXXgXXXXh' OK
string_replace_all('xaxbcdxefxgxhx', 'x', 'XXXX') => 6 'XXXXaXXXXbcdXXXXefXXXXgXXXXhXXXX' OK
string_replace_all('abxdxefgxhixj', 'x', 'XX') => 4 'abXXdXXefgXXhiXXj' OK
string_replace_all('xxabxxcdxexfx', 'x', 'XX') => 7 'XXXXabXXXXcdXXeXXfXX' OK
string_replace_all('abxxdexxxxgh', 'xx', 'X') => 3 'abXdeXXgh' OK
string_replace_all('xxabxxxxcdxxefxx', 'xx', 'X') => 5 'XabXXcdXefX' OK
string_replace_all('abxxxcdxexf', 'x', '(null)') => 5 'abcdef' OK
string_replace_all('xxabxxxcdxefxx', 'x', '(null)') => 8 'abcdef' OK
string_replace_all('xxxxxxxxxxxxxxx', 'x', '(null)') => 15 '' OK
string_replace_all('abxxcxdexxxxf', 'xx', 'XX') => 3 'abXXcxdeXXXXf' OK
string_replace_all('xxabxxxcxxxxx', 'xx', 'XX') => 4 'XXabXXxcXXXXx' OK
string_replace_all('abxxcd', 'x', 'x') => 0 'abxxcd' OK
© www.soinside.com 2019 - 2024. All rights reserved.