【问题标题】:How to replace all occurrences of a character in string?如何替换字符串中所有出现的字符?
【发布时间】:2011-02-23 04:57:48
【问题描述】:

std::string中的另一个字符替换所有出现的字符的有效方法是什么?

【问题讨论】:

  • 当谈到这种“高级”功能时,stdlib 似乎很糟糕。当你开始寻找丢失的东西时,最好使用 QString 或通用库。

标签: c++ algorithm str-replace stdstring


【解决方案1】:

std::string 不包含此类函数,但您可以使用来自 algorithm 标头的独立 replace 函数。

#include <algorithm>
#include <string>

void some_func() {
  std::string s = "example string";
  std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}

【讨论】:

  • std::string一个容器,专门设计用于处理字符序列。 link
  • 不幸的是,这仅允许将一个字符替换为另一个字符。它不能用更多的字符(即字符串)替换一个字符。有没有办法用更多字符进行搜索替换?
  • @Kirill V. Lyadvinsky 如果我只想删除一个事件该怎么办。
  • @KirillV.Lyadvinsky:当我使用这种方法将所有 x 替换为 y 时,无论原始字符串是什么,结果都是一个冗长的 y 字符串。我很好奇你认为会是什么问题。 (代码和你写的一模一样)
  • @Transcendent:这正是std::string::replace() 而不是std::replace() 所发生的事情! 'x' (char) 被隐式转换为 size_t [值 120],因此整个字符串或部分字符串将被 120 个 'y' 副本填充。
【解决方案2】:

正如基里尔建议的那样,要么使用替换方法,要么沿着字符串迭代,独立替换每个字符。

或者,您可以根据需要使用find 方法或find_first_of。这些解决方案都不会一次性完成这项工作,但是您应该通过几行额外的代码让它们为您工作。 :-)

【讨论】:

    【解决方案3】:

    单个字符的简单查找和替换如下所示:

    s.replace(s.find("x"), 1, "y")

    要对整个字符串执行此操作,最简单的方法是循环直到您的s.find 开始返回npos。我想你也可以抓住range_error 退出循环,但这有点难看。

    【讨论】:

    • 虽然当要替换的字符数与字符串长度相比较小时,这可能是一个合适的解决方案,但它不能很好地扩展。随着原字符串中需要替换的字符比例的增加,这种方法在时间上会逼近O(N^2)。
    • 是的。我的总体理念是做简单的事情(写和读),直到效率低下导致真正的问题。在某些情况下,您可能会使用 O(N**2) 很重要的巨大字符串,但 99% 的时间我的字符串都是 1K 或更少。
    • ...话虽如此,我更喜欢基里尔的方法(并且已经投票赞成)。
    • 如果找不到“x”会怎样?另外,为什么要使用双括号?
    • @PrasathGovind - 我只是在显示所需的调用(因此“类似”)。重要但晦涩的细节,例如正确的错误处理,留给读者练习。至于“双括号”,我不确定它们是什么,或者你在说什么。对我来说,“大括号”是{ 字符。我不知道什么是“双括号”。也许你有某种字体问题?
    【解决方案4】:

    我想我也会扔boost solution

    #include <boost/algorithm/string/replace.hpp>
    
    // in place
    std::string in_place = "blah#blah";
    boost::replace_all(in_place, "#", "@");
    
    // copy
    const std::string input = "blah#blah";
    std::string output = boost::replace_all_copy(input, "#", "@");
    

    【讨论】:

    • 那么您的编译器缺少一些 -I 标志,以便它可以在您的系统上找到 Boost 库。也许您甚至需要先安装它。
    • 上面的更有效,因为它带有std lib。没有都使用boost库;-)
    【解决方案5】:

    问题集中在character 替换上,但是,由于我发现这个页面非常有用(尤其是Konrad 的评论),我想分享这个更通用的实现,它允许处理@987654323 @还有:

    std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
        size_t start_pos = 0;
        while((start_pos = str.find(from, start_pos)) != std::string::npos) {
            str.replace(start_pos, from.length(), to);
            start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
        }
        return str;
    }
    

    用法:

    std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
    std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
    std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;
    

    输出:

    Number_Of_Beans

    XXjXugtXty

    hhjhugthty


    编辑:

    以上可以以更合适的方式实现,如果您关心性能,则不返回任何内容 (void) 并直接对作为参数给出的字符串 str 执行更改,传递 按地址,而不是按值。这将避免原始字符串的无用且昂贵的副本,同时返回结果。你的电话,然后...

    代码:

    static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
    {
        // Same inner code...
        // No return statement
    }
    

    希望这对其他人有所帮助...

    【讨论】:

    • 这个在源字符串很大并且要替换的字符串出现很多的情况下会出现性能问题。 string::replace() 会被调用很多次,这会导致大量的字符串副本。请参阅我解决该问题的解决方案。
    • 尼特挑剔:按地址 => 按参考。是否是地址是实现细节。
    • 你应该实际检查from字符串是否为空,否则会出现死循环。
    【解决方案6】:
    #include <iostream>
    #include <string>
    using namespace std;
    // Replace function..
    string replace(string word, string target, string replacement){
        int len, loop=0;
        string nword="", let;
        len=word.length();
        len--;
        while(loop<=len){
            let=word.substr(loop, 1);
            if(let==target){
                nword=nword+replacement;
            }else{
                nword=nword+let;
            }
            loop++;
        }
        return nword;
    
    }
    //Main..
    int main() {
      string word;
      cout<<"Enter Word: ";
      cin>>word;
      cout<<replace(word, "x", "y")<<endl;
      return 0;
    }
    

    【讨论】:

    • 如果word很长,调用函数时可能会有很多开销。您可以通过传递 wordtargetreplacement 作为 const 引用来优化它。
    【解决方案7】:

    想象一个大型二进制 blob,其中所有 0x00 字节都应替换为“\1\x30”,所有 0x01 字节应替换为“\1\x31”,因为传输协议不允许使用 \0 字节。

    在以下情况下:

    • 替换字符串和待替换字符串的长度不同,
    • 源字符串中有很多次要替换的字符串,并且
    • 源字符串很大,

    所提供的解决方案无法应用(因为它们仅替换单个字符)或存在性能问题,因为它们会调用 string::replace 多次,这会一遍又一遍地生成 blob 大小的副本。 (不知道boost的方案,从那个角度来说可能还可以)

    这个遍历源字符串中的所有出现并逐段构建新字符串一次

    void replaceAll(std::string& source, const std::string& from, const std::string& to)
    {
        std::string newString;
        newString.reserve(source.length());  // avoids a few memory allocations
    
        std::string::size_type lastPos = 0;
        std::string::size_type findPos;
    
        while(std::string::npos != (findPos = source.find(from, lastPos)))
        {
            newString.append(source, lastPos, findPos - lastPos);
            newString += to;
            lastPos = findPos + from.length();
        }
    
        // Care for the rest after last occurrence
        newString += source.substr(lastPos);
    
        source.swap(newString);
    }
    

    【讨论】:

    • 这是迄今为止最好的解决方案,它仅建立在 STL 之上。如果您要添加一个自定义函数以便在任何地方轻松使用,请使用它。
    • 最好使用newString.append(source, lastPos, source.length() - lastPos); 而不是newString += source.substr(lastPos); 以避免创建临时字符串[正如substr() 所做的那样]。 (我无法编辑此答案,因为建议的编辑队列已满。)
    【解决方案8】:

    如果您要替换多个字符,并且只处理 std::string,那么这个 sn-p 将起作用,将 sHaystack 中的 sNeedle 替换为 sReplace,并且 sNeedle 和 sReplace 不需要是大小相同。此例程使用 while 循环替换所有匹配项,而不仅仅是从左到右找到的第一个匹配项。

    while(sHaystack.find(sNeedle) != std::string::npos) {
      sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
    }
    

    【讨论】:

    • 这是 O(n^)。你可以在 O(n) 时间内完成。
    • @ChangmingSun 你指的是哪个 O(n) 解决方案?
    • 如果 kNeedle 恰好是 sReplace 的子字符串,这将无限循环。
    • 另外还有两次find 呼叫。考虑将该结果作为临时变量。
    【解决方案9】:

    这行得通!我在书店应用程序中使用了与此类似的东西,其中库存存储在 CSV(如 .dat 文件)中。但在单字符的情况下,意味着替换器只是单字符,例如'|',它必须用双引号“|”为了不抛出无效的转换 const char。

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        int count = 0;  // for the number of occurences.
        // final hold variable of corrected word up to the npos=j
        string holdWord = "";
        // a temp var in order to replace 0 to new npos
        string holdTemp = "";
        // a csv for a an entry in a book store
        string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";
    
        // j = npos
        for (int j = 0; j < holdLetter.length(); j++) {
    
            if (holdLetter[j] == ',') {
    
                if ( count == 0 ) 
                {           
                    holdWord = holdLetter.replace(j, 1, " | ");      
                }
                else {
    
                    string holdTemp1 = holdLetter.replace(j, 1, " | ");
    
                    // since replacement is three positions in length,
                    // must replace new replacement's 0 to npos-3, with
                    // the 0 to npos - 3 of the old replacement 
                    holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3); 
    
                    holdWord = "";
    
                    holdWord = holdTemp;
    
                }
                holdTemp = "";
                count++;
            }
        } 
        cout << holdWord << endl;
        return 0;
    }
    
    // result:
    Big Java 7th Ed | Horstman | 978-1118431115 | 99.85
    

    不习惯我目前使用的是 CentOS,所以我的编译器版本低于 . C++版本(g++),C++98默认:

    g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
    Copyright (C) 2015 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

    【讨论】:

      【解决方案10】:

      如果你愿意使用std::strings,你可以按原样使用这个示例应用程序的strsub函数,或者如果你希望它采用不同的类型或一组参数来大致实现相同的目标。基本上,它使用std::string 的属性和功能来快速擦除匹配的字符集,并直接在std::string 中插入所需的字符。每次执行此替换操作时,如果仍然可以找到要替换的匹配字符,则偏移量会更新,如果由于没有其他要替换的内容而无法替换,则返回上次更新时的字符串状态。

      #include <iostream>
      #include <string>
      
      std::string strsub(std::string stringToModify,
                         std::string charsToReplace,
                         std::string replacementChars);
      
      int main()
      {
          std::string silly_typos = "annoiiyyyng syyyllii tiipos.";
      
          std::cout << "Look at these " << silly_typos << std::endl;
          silly_typos = strsub(silly_typos, "yyy", "i");
          std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
          silly_typos = strsub(silly_typos, "ii", "y");
      
          std::cout << "There, no more " << silly_typos << std::endl;
          return 0;
      }
      
      std::string strsub(std::string stringToModify,
                         std::string charsToReplace,
                         std::string replacementChars)
      {
          std::string this_string = stringToModify;
      
          std::size_t this_occurrence = this_string.find(charsToReplace);
          while (this_occurrence != std::string::npos)
          {
              this_string.erase(this_occurrence, charsToReplace.size());
              this_string.insert(this_occurrence, replacementChars);
              this_occurrence = this_string.find(charsToReplace,
                                                 this_occurrence + replacementChars.size());
          }
      
          return this_string;
      }
      

      如果您不想依赖使用 std::strings 作为参数来传递 C 风格的字符串,您可以在下面查看更新后的示例:

      #include <iostream>
      #include <string>
      
      std::string strsub(const char * stringToModify,
                         const char * charsToReplace,
                         const char * replacementChars,
                         uint64_t sizeOfCharsToReplace,
                         uint64_t sizeOfReplacementChars);
      
      int main()
      {
          std::string silly_typos = "annoiiyyyng syyyllii tiipos.";
      
          std::cout << "Look at these " << silly_typos << std::endl;
          silly_typos = strsub(silly_typos.c_str(), "yyy", "i", 3, 1);
          std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
          silly_typos = strsub(silly_typos.c_str(), "ii", "y", 2, 1);
      
          std::cout << "There, no more " << silly_typos << std::endl;
          return 0;
      }
      
      std::string strsub(const char * stringToModify,
                         const char * charsToReplace,
                         const char * replacementChars,
                         uint64_t sizeOfCharsToReplace,
                         uint64_t sizeOfReplacementChars)
      {
          std::string this_string = stringToModify;
      
          std::size_t this_occurrence = this_string.find(charsToReplace);
          while (this_occurrence != std::string::npos)
          {
              this_string.erase(this_occurrence, sizeOfCharsToReplace);
              this_string.insert(this_occurrence, replacementChars);
              this_occurrence = this_string.find(charsToReplace,
                  this_occurrence + sizeOfReplacementChars);
          }
      
          return this_string;
      }
      

      【讨论】:

        【解决方案11】:

        对于简单的情况,这在不使用任何其他库的情况下效果很好,然后是 std::string(已经在使用)。

        some_string中所有出现的字符a替换为字符b

        for (size_t i = 0; i < some_string.size(); ++i) {
            if (some_string[i] == 'a') {
                some_string.replace(i, 1, "b");
            }
        }
        

        如果字符串很大或多次调用替换是一个问题,您可以应用此答案中提到的技术:https://stackoverflow.com/a/29752943/3622300

        【讨论】:

          【解决方案12】:

          老派 :-)

          std::string str = "H:/recursos/audio/youtube/libre/falta/"; 
          
          for (int i = 0; i < str.size(); i++) {
              if (str[i] == '/') {
                  str[i] = '\\';
              }
          }
          
          std::cout << str;
          

          结果:

          H:\recursos\audio\youtube\libre\falta\

          【讨论】:

            【解决方案13】:

            Abseil StrReplaceAll 呢?从头文件:

            // This file defines `absl::StrReplaceAll()`, a general-purpose string
            // replacement function designed for large, arbitrary text substitutions,
            // especially on strings which you are receiving from some other system for
            // further processing (e.g. processing regular expressions, escaping HTML
            // entities, etc.). `StrReplaceAll` is designed to be efficient even when only
            // one substitution is being performed, or when substitution is rare.
            //
            // If the string being modified is known at compile-time, and the substitutions
            // vary, `absl::Substitute()` may be a better choice.
            //
            // Example:
            //
            // std::string html_escaped = absl::StrReplaceAll(user_input, {
            //                                                {"&", "&amp;"},
            //                                                {"<", "&lt;"},
            //                                                {">", "&gt;"},
            //                                                {"\"", "&quot;"},
            //                                                {"'", "&#39;"}});
            

            【讨论】:

              【解决方案14】:

              这是我以最大 DRI 精神推出的解决方案。 它将在 sHaystack 中搜索 sNeedle 并将其替换为 sReplace, nTimes 如果非 0,否则所有 sNeedle 出现。 它不会在替换的文本中再次搜索。

              std::string str_replace(
                  std::string sHaystack, std::string sNeedle, std::string sReplace, 
                  size_t nTimes=0)
              {
                  size_t found = 0, pos = 0, c = 0;
                  size_t len = sNeedle.size();
                  size_t replen = sReplace.size();
                  std::string input(sHaystack);
              
                  do {
                      found = input.find(sNeedle, pos);
                      if (found == std::string::npos) {
                          break;
                      }
                      input.replace(found, len, sReplace);
                      pos = found + replen;
                      ++c;
                  } while(!nTimes || c < nTimes);
              
                  return input;
              }
              

              【讨论】:

                【解决方案15】:

                为了完整起见,下面是使用std::regex 的方法。

                #include <regex>
                #include <string>
                
                int main()
                {
                    const std::string s = "example string";
                    const std::string r = std::regex_replace(s, std::regex("x"), "y");
                }
                

                【讨论】:

                  【解决方案16】:

                  这不是标准库中唯一缺少的方法,它本来是低级的。 这个用例和许多其他用例都包含在通用库中,例如:

                  QtCore & QString 有我的偏好:它支持 UTF8 并使用更少的模板,这意味着可以理解的错误和更快的编译。它使用“q”前缀,这使得命名空间变得不必要并简化了标题。
                  Boost 通常会产生可怕的错误消息和缓慢的编译时间。
                  POCO 似乎是一个合理的妥协。

                  【讨论】:

                    【解决方案17】:

                    我想我会使用std::replace_if()

                    可以使用标准库函数编写一个简单的字符替换器(由 OP 请求)。

                    对于就地版本:

                    #include <string>
                    #include <algorithm>
                    
                    void replace_char(std::string& in,
                                      std::string::value_type srch,
                                      std::string::value_type repl)
                    {
                        std::replace_if(std::begin(in), std::end(in),
                                        [&srch](std::string::value_type v) { return v==srch; },
                                        repl);
                        return;
                    }
                    

                    如果输入是const 字符串,则返回一个副本的重载:

                    std::string replace_char(std::string const& in,
                                             std::string::value_type srch,
                                             std::string::value_type repl)
                    {
                        std::string result{ in };
                        replace_char(result, srch, repl);
                        return result;
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2011-04-05
                      • 2012-08-01
                      • 1970-01-01
                      • 2022-01-19
                      相关资源
                      最近更新 更多