【问题标题】:How to find and replace all occurrences of a substring in a string?如何查找和替换字符串中所有出现的子字符串?
【发布时间】:2013-12-22 19:00:02
【问题描述】:

我需要搜索一个字符串并编辑它的格式。

到目前为止,我可以替换第一次出现的字符串,但我无法用该字符串的下一次出现来替换。

这就是我的工作,有点:

if(chartDataString.find("*A") == string::npos){ return;}
else{chartDataString.replace(chartDataString.find("*A"), 3,"[A]\n");}

如果它没有找到字符串,则什么都不会打印,所以这不好。

我知道我需要遍历整个字符串 chartDataString 并替换所有匹配项。我知道有很多类似的帖子,但我不明白(比如这个Replace substring with another substring C++

我也尝试过这样做来循环字符串:

string toSearch = chartDataString;
string toFind = "*A:";
for (int i = 0; i<toSearch.length() - toFind.length(); i++){
   if(toSearch.substr(i, toFind.length()) == toFind){
       chartDataString.replace(chartDataString.find(toFind), 3, "[A]\n");   
   }
}

编辑 考虑到建议,理论上这应该可行,但我不知道为什么不这样做

size_t startPos=0;
string myString = "*A";
while(string::npos != (startPos = chartDataString.find(myString, startPos))){
    chartDataString.replace(chartDataString.find(myString, startPos), 3, "*A\n");
    startPos = startPos + myString.length();
}   

【问题讨论】:

  • 使用std::regexboost::regex怎么样?
  • Boost 有一个replace_all 函数。

标签: c++ string algorithm str-replace


【解决方案1】:

试试下面的

const std::string s = "*A";
const std::string t = "*A\n";

std::string::size_type n = 0;
while ( ( n = chartDataString.find( s, n ) ) != std::string::npos )
{
    chartDataString.replace( n, s.size(), t );
    n += t.size();
}

【讨论】:

  • 但是如果 string 是 1MB 并且被替换的 char 在里面找到了 100 次,那么这意味着将 100MB 分配给 100bytes。
【解决方案2】:

如果boost 可用,您可以使用以下内容:

std::string origStr = "this string has *A and then another *A";
std::string subStringToRemove = "*A";
std::string subStringToReplace = "[A]";

boost::replace_all(origStr , subStringToRemove , subStringToReplace);

对原始字符串进行修改,或者

std::string result = boost::replace_all_copy(origStr , subStringToRemove , subStringToReplace);

在不修改原始字符串的情况下执行修改。

【讨论】:

    【解决方案3】:

    使用 C++11 提供的 std::regex_replace。这正是您想要的,甚至更多。

    https://en.cppreference.com/w/cpp/regex/regex_replace

    std::string const result = std::regex_replace( chartDataString, std::regex( "\\*A" ), "[A]\n" );
    

    【讨论】:

      【解决方案4】:
      /// Returns a version of 'str' where every occurrence of
      /// 'find' is substituted by 'replace'.
      /// - Inspired by James Kanze.
      /// - http://stackoverflow.com/questions/20406744/
      std::string replace_all(
          const std::string & str ,   // where to work
          const std::string & find ,  // substitute 'find'
          const std::string & replace //      by 'replace'
      ) {
          using namespace std;
          string result;
          size_t find_len = find.size();
          size_t pos,from=0;
          while ( string::npos != ( pos=str.find(find,from) ) ) {
              result.append( str, from, pos-from );
              result.append( replace );
              from = pos + find_len;
          }
          result.append( str, from , string::npos );
          return result;
      /*
          This code might be an improvement to James Kanze's
          because it uses std::string methods instead of
          general algorithms [as 'std::search()'].
      */
      }
      
      int main() {
          {
              std::string test    = "*A ... *A ... *A ...";
              std::string changed = "*A\n ... *A\n ... *A\n ...";
      
              assert( changed == replace_all( test, "*A", "*A\n" ) );
          }
          {
              std::string GB = "My gorila ate the banana";
      
              std::string gg = replace_all( GB, "gorila", "banana" );
              assert( gg ==  "My banana ate the banana" );
              gg = replace_all( gg, "banana", "gorila"  );
              assert( gg ==  "My gorila ate the gorila" );
      
              std::string bb = replace_all( GB, "banana", "gorila" );
              assert( gg ==  "My gorila ate the gorila" );
              bb = replace_all( bb, "gorila" , "banana" );
              assert( bb ==  "My banana ate the banana" );
          }
          {
              std::string str, res;
      
              str.assign( "ababaabcd" );
              res = replace_all( str, "ab", "fg");
              assert( res == "fgfgafgcd" );
      
              str="aaaaaaaa"; assert( 8==str.size() );
              res = replace_all( str, "aa", "a" );
              assert( res == "aaaa" );
              assert( "" == replace_all( str, "aa", "" ) );
      
              str = "aaaaaaa"; assert( 7==str.size() );
              res = replace_all( str, "aa", "a" );
              assert( res == "aaaa" );
      
              str = "..aaaaaa.."; assert( 10==str.size() );
              res = replace_all( str, "aa", "a" );
              assert( res == "..aaa.." );
      
              str = "baaaac"; assert( 6==str.size() );
              res = replace_all( str, "aa", "" );
              assert( res == "bc" );
          }
      }
      

      【讨论】:

        【解决方案5】:

        find 函数采用可选的第二个参数:开始搜索的位置。默认情况下为零。

        开始搜索下一个匹配的好位置是插入前一个替换的位置,加上该替换的长度。例如,如果我们在位置 7 处插入一个长度为 3 的字符串,那么下一个 find 应该从位置 10 开始。

        如果搜索字符串恰好是替换的子字符串,这种方法将避免无限循环。想象一下,如果您尝试用analog 替换所有出现的log,但不要跳过替换。

        【讨论】:

        • 如何找出第一次出现的位置是什么?
        • @Sarah 您已经在您的示例代码中使用它来执行第一次替换!是find的返回值。
        • 算法类似于 1. 将位置初始化为零; 2. 找到从该位置开始的子串。 3.如果没有找到子字符串,你就完成了。否则: 4. 替换该位置的子串。 5.将替换字符串的长度添加到位置。 6. 返回第 2 步。
        【解决方案6】:

        这样做相当尴尬(而且可能效率不高) 地方。我通常使用以下功能:

        std::string
        replaceAll( std::string const& original, std::string const& from, std::string const& to )
        {
            std::string results;
            std::string::const_iterator end = original.end();
            std::string::const_iterator current = original.begin();
            std::string::const_iterator next = std::search( current, end, from.begin(), from.end() );
            while ( next != end ) {
                results.append( current, next );
                results.append( to );
                current = next + from.size();
                next = std::search( current, end, from.begin(), from.end() );
            }
            results.append( current, next );
            return results;
        }
        

        基本上,只要你能找到一个实例,你就会循环 from,附加中间文本和to,并推进 到from 的下一个实例。最后,您附加任何文本 在from 的最后一个实例之后。

        (如果您要使用 C++ 进行大量编程,则可能是 习惯使用迭代器是个好主意,就像上面一样, 而不是std::string 的特殊成员函数。 可以使上述内容适用于任何 C++ 容器类型,因此,更惯用。)

        【讨论】:

        • 我真的很想使用它,但我认为它不起作用,我不确定如何使它起作用。当我调用 replaceAll(chartDataString, "*A", "[A]\n");什么都没有发生,我不知道如何让它发挥作用
        • @Sarah 它对我有用。 (实际上,它是从生产代码中采用的。)您确定chartDataString 包含序列"*A" 吗?你在看返回值吗?这个函数不会改变originalfromto 中的任何内容? (一般来说,使用纯函数比使用修改现有对象的函数要干净得多。性能有时会有所不同,但作为一般规则,更喜欢纯函数而不是变异函数。)
        【解决方案7】:

        如果您需要反转的字符串大小不同:

        void            Replace::replace(std::string & str, std::string const & s1, std::string const & s2)
        {
            size_t      pos;
        
            pos = 0;
            while ((pos = str.find(s1, pos)) != std::string::npos)
            {
                str.erase(pos, s1.length());
                str.insert(pos, s2);
                pos += s2.length();
            }
            return ;
        }
        

        【讨论】:

          【解决方案8】:

          string replaceAll(string del, string replace, string line){

          int len=del.length();
          string output="[Programming Error]";
          if(line.find(del)!=-1){
              do{
                  output=line.replace(line.find(del),len,replace);
              }while(output.find(del)!=-1);
          }
          return output;
          

          }

          【讨论】:

            【解决方案9】:

            以下是findstring::replacereplace 工作原理的完整展示。

            cpp中没有replaceAll的直接实现。

            我们可以调整replace 来执行我们的意图:

            string original = "A abc abc abc A";
            string test = original;
            cout << endl << "Original string: " << original;  //output: A abc abc abc A
            
            //FINDING INDEX WHERE QUERY SUBSTRING FOUND
            int index = test.find("a");
            cout << endl << "index: " << index; //output: 2
            int outOfBoundIndex = test.find("xyz");
            cout << endl << "outOfBoundIndex: " << outOfBoundIndex; //output: -1
            
            //REPLACE SINGLE OCCURENCES
            string queryString = "abc";
            int queryStringLength = queryString.size();
            index = test.find(queryString);
            if(index > -1 && index < (test.size() - 1))
                test.replace(index, queryStringLength, "xyz");
            
            cout << endl << endl << "first occurrence \'abc\' replaced to \'xyz\': " << test;  //output: A xyz abc abc A
            
            //REPLACE ALL OCCURRENCES
            test = original;
            
            //there is a cpp utility function to replace all occurrence of single character. It will not work for replacing all occurences of string.
            replace(test.begin(), test.end(), 'a', 'X');
            cout << endl << endl << "Replacing all occurences of character \'a\' with \'X\': " << test;  //output: A Xbc Xbc Xbc A
            test = original;
            index = test.find("abc");
            while(index > -1 && index < (test.size() - 1)){
                test.replace(index, queryStringLength, "xyz");
                index = test.find("abc");
            }
            cout << endl << "replaceAll implementation: " << test;  //output: A xyz xyz xyz A
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-11-20
              • 1970-01-01
              • 2013-03-22
              • 2015-07-03
              • 2013-01-07
              • 2011-02-23
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多