【问题标题】:How to remove certain characters from a string in C++?如何从 C++ 中的字符串中删除某些字符?
【发布时间】:2023-03-14 19:45:01
【问题描述】:

例如,我让用户输入电话号码。

cout << "Enter phone number: ";
INPUT: (555) 555-5555
cin >> phone;

我想从字符串中删除“(”、“)”和“-”字符。我查看了字符串删除、查找和替换函数,但我只看到它们基于位置进行操作。

是否有一个字符串函数可以用来传递一个字符,例如“(”,并让它删除字符串中的所有实例?

【问题讨论】:

    标签: c++ string character


    【解决方案1】:
       string str("(555) 555-5555");
    
       char chars[] = "()-";
    
       for (unsigned int i = 0; i < strlen(chars); ++i)
       {
          // you need include <algorithm> to use general algorithms like std::remove()
          str.erase (std::remove(str.begin(), str.end(), chars[i]), str.end());
       }
    
       // output: 555 5555555
       cout << str << endl;
    

    用作函数

    void removeCharsFromString( string &str, char* charsToRemove ) {
       for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) {
          str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() );
       }
    }
    //example of usage:
    removeCharsFromString( str, "()-" );
    

    【讨论】:

    • 这是如何工作的?使用擦除和删除不是双重否定吗?对我来说,这就是:“擦除 ()- 不存在的位置的字符。”而且由于每一个都是一次完成的,它不应该删除所有字符吗?我已经阅读了这两个函数的文档,这对我来说毫无意义。 cplusplus.com/reference/algorithm/removecplusplus.com/reference/string/string/erase
    • @Brent 和未来的读者,这是Erase-remove idiom。简而言之,std::remove 将未删除的项移动到向量的前面,并返回一个迭代器,该迭代器指向最后一个未删除的项。然后std::erase 将向量从该迭代器修剪到末尾。
    • 对于真正的 C++ 版本,我认为我们应该使用 string chars("()-"); 然后使用 .length() 方法来获取长度和 .at(i) 方法来访问字符 :) 功能化小提琴 - ideone.com/tAZt5I跨度>
    • 用作函数: ideone.com/XOROjq - 使用&lt;iostream&gt; &lt;algorithm&gt; &lt;cstring&gt;
    • 你最好缓存 strlen(chars) 因为它有 O(n) 复杂度
    【解决方案2】:

    我想删除“(”、“)”和“-” 字符串中的字符。

    您可以使用std::remove_if() 算法仅删除您指定的字符:

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    bool IsParenthesesOrDash(char c)
    {
        switch(c)
        {
        case '(':
        case ')':
        case '-':
            return true;
        default:
            return false;
        }
    }
    
    int main()
    {
        std::string str("(555) 555-5555");
        str.erase(std::remove_if(str.begin(), str.end(), &IsParenthesesOrDash), str.end());
        std::cout << str << std::endl; // Expected output: 555 5555555
    }
    

    std::remove_if() 算法需要一个叫做谓词的东西,它可以是一个函数指针,就像上面的 sn-p 一样。

    你也可以传递一个函数对象(一个重载函数调用()操作符的对象)。这使我们能够创建一个更通用的解决方案:

    #include <iostream>
    #include <algorithm>
    #include <string>
    
    class IsChars
    {
    public:
        IsChars(const char* charsToRemove) : chars(charsToRemove) {};
    
        bool operator()(char c)
        {
            for(const char* testChar = chars; *testChar != 0; ++testChar)
            {
                if(*testChar == c) { return true; }
            }
            return false;
        }
    
    private:
        const char* chars;
    };
    
    int main()
    {
        std::string str("(555) 555-5555");
        str.erase(std::remove_if(str.begin(), str.end(), IsChars("()- ")), str.end());
        std::cout << str << std::endl; // Expected output: 5555555555
    }
    

    您可以使用"()- " 字符串指定要删除的字符。在上面的示例中,我添加了一个空格,以便删除空格以及括号和破折号。

    【讨论】:

    • 你也可以使用ispunct(int c)
    • 出色的实施。这种方法完美无缺,并且有很大的进一步发展空间。感谢您的答复。 MSalters,我还将查找 ispunct(int c) 函数并报告我的工作情况。
    【解决方案3】:

    remove_if() 已经被提及。但是,对于 C++0x,您可以改为使用 lambda 为其指定谓词。

    下面是一个例子,它有 3 种不同的过滤方式。当您使用 const 或不想修改原始版本时,也包含函数的“复制”版本。

    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <cctype>
    using namespace std;
    
    string& remove_chars(string& s, const string& chars) {
        s.erase(remove_if(s.begin(), s.end(), [&chars](const char& c) {
            return chars.find(c) != string::npos;
        }), s.end());
        return s;
    }
    string remove_chars_copy(string s, const string& chars) {
        return remove_chars(s, chars);
    }
    
    string& remove_nondigit(string& s) {
        s.erase(remove_if(s.begin(), s.end(), [](const char& c) {
            return !isdigit(c);
        }), s.end());
        return s;
    }
    string remove_nondigit_copy(string s) {
        return remove_nondigit(s);
    }
    
    string& remove_chars_if_not(string& s, const string& allowed) {
        s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) {
            return allowed.find(c) == string::npos;
        }), s.end());
        return s;
    }
    string remove_chars_if_not_copy(string s, const string& allowed) {
        return remove_chars_if_not(s, allowed);
    }
    
    int main() {
        const string test1("(555) 555-5555");
        string test2(test1);
        string test3(test1);
        string test4(test1);
        cout << remove_chars_copy(test1, "()- ") << endl;
        cout << remove_chars(test2, "()- ") << endl;
        cout << remove_nondigit_copy(test1) << endl;
        cout << remove_nondigit(test3) << endl;
        cout << remove_chars_if_not_copy(test1, "0123456789") << endl;
        cout << remove_chars_if_not(test4, "0123456789") << endl;
    }
    

    【讨论】:

    • 我应该真正使用 const string::value_type& 而不是 const char& c。但是,在这种情况下,这没什么大不了的。
    • 这是一个非常彻底的实现。我很感激,也会使用这个实现。
    【解决方案4】:

    对于任何感兴趣的人来说,这是一个不同的解决方案。它使用 c++11 中的新 For 范围

    string str("(555) 555-5555");
    string str2="";
    
    for (const auto c: str){
    
        if(!ispunct(c)){
    
            str2.push_back(c);
        }
    }
    
    str = str2;
    //output: 555 5555555
    cout<<str<<endl;
    

    【讨论】:

    • (1) str2 不需要初始化。 (2) str = std::move(str2) 会更有效率。
    【解决方案5】:

    恐怕 std::string 没有这样的成员,但你可以轻松地编写那种函数。 这可能不是最快的解决方案,但这已经足够了:

    std::string RemoveChars(const std::string& source, const std::string& chars) {
       std::string result="";
       for (unsigned int i=0; i<source.length(); i++) {
          bool foundany=false;
          for (unsigned int j=0; j<chars.length() && !foundany; j++) {
             foundany=(source[i]==chars[j]);
          }
          if (!foundany) {
             result+=source[i];
          }
       }
       return result;
    }
    

    编辑:阅读下面的答案,我理解它更笼统,而不仅仅是检测数字。上述解决方案将省略第二个参数字符串中传递的每个字符。 例如:

    std::string result=RemoveChars("(999)99-8765-43.87", "()-");
    

    会导致

    99999876543.87
    

    【讨论】:

      【解决方案6】:
      using namespace std;
      
      
      // c++03
      string s = "(555) 555-5555";
      s.erase(remove_if(s.begin(), s.end(), not1(ptr_fun(::isdigit))), s.end());
      
      // c++11
      s.erase(remove_if(s.begin(), s.end(), ptr_fun(::ispunct)), s.end());
      

      注意:你可能需要写ptr_fun&lt;int, int&gt;而不是简单的ptr_fun

      【讨论】:

      • 这怎么不是选择的答案?
      • @user3240688 请注意,std::ptr_fun 在 C++11 中已弃用,将在 C++17 中删除,std::not1 在 C++17 中已弃用。您可以使用 std::crefstd::function(或 lambdas)。
      【解决方案7】:

      是的,您可以使用 isdigit() 函数来检查数字:)

      给你:

      #include <iostream>
      #include <cctype>
      #include <string.h>
      
      using namespace std;
      
      int main(){
      
        char *str = "(555) 555-5555";
        int len = strlen(str);
      
        for (int i=0; i<len; i++){
            if (isdigit(*(str+i))){
              cout << *(str+i);
            }
        }
      
        cout << endl;
      
      
      return 0;   
      }
      

      希望对你有帮助:)

      【讨论】:

      • 这可以被修改以移除返回false的元素。谢谢。
      【解决方案8】:

      boost::is_any_of

      去除一个字符串中出现在另一个给定字符串中的所有字符:

      #include <cassert>
      
      #include <boost/range/algorithm/remove_if.hpp>
      #include <boost/algorithm/string/classification.hpp>
      
      int main() {
          std::string str = "a_bc0_d";
          str.erase(boost::remove_if(str, boost::is_any_of("_0")), str.end());
          assert((str == "abcd"));
      }
      

      在 Ubuntu 16.04、Boost 1.58 中测试。

      【讨论】:

        【解决方案9】:

        如果你有一个支持可变参数模板的编译器,你可以使用这个:

        #include <iostream>
        #include <string>
        #include <algorithm>
        
        template<char ... CharacterList>
        inline bool check_characters(char c) {
            char match_characters[sizeof...(CharacterList)] = { CharacterList... };
            for(int i = 0; i < sizeof...(CharacterList); ++i) {
                if(c == match_characters[i]) {
                    return true;
                }
            }
            return false;
        }
        
        template<char ... CharacterList>
        inline void strip_characters(std::string & str) {
            str.erase(std::remove_if(str.begin(), str.end(), &check_characters<CharacterList...>), str.end());
        }
        
        int main()
        {
            std::string str("(555) 555-5555");
            strip_characters< '(',')','-' >(str);
            std::cout << str << std::endl;
        }
        

        【讨论】:

          【解决方案10】:

          这是另一种选择:

          template<typename T>
          void Remove( std::basic_string<T> & Str, const T * CharsToRemove )
          {
              std::basic_string<T>::size_type pos = 0;
              while (( pos = Str.find_first_of( CharsToRemove, pos )) != std::basic_string<T>::npos )
              {
                  Str.erase( pos, 1 ); 
              }
          }
          
          std::string a ("(555) 555-5555");
          Remove( a, "()-");
          

          适用于 std::string 和 std::wstring

          【讨论】:

            【解决方案11】:

            我是新手,但上面的一些答案非常复杂,所以这里有一个替代方案。

            注意:只要 0-9 是连续的(它们应该符合标准),这应该过滤掉除数字和 ' ' 之外的所有其他字符。知道 0-9 应该是连续的并且 char 确实是 int,我们可以执行以下操作。

            编辑:我没有注意到海报也想要空间,所以我改变了它......

            #include <cstdio>
            #include <cstring>
            
            void numfilter(char * buff, const char * string)
            {
              do
              { // According to standard, 0-9 should be contiguous in system int value.
                if ( (*string >= '0' && *string <= '9') || *string == ' ')
                  *buff++ = *string;
              } while ( *++string );
              *buff++ = '\0'; // Null terminate
            }
            
            int main()
            {
              const char *string = "(555) 555-5555";
              char buff[ strlen(string) + 1 ];
            
              numfilter(buff, string);
              printf("%s\n", buff);
            
            return 0;
            }
            

            下面是过滤提供的字符。

            #include <cstdio>
            #include <cstring>
            
            void cfilter(char * buff, const char * string, const char * toks)
            {
              const char * tmp;  // So we can keep toks pointer addr.
              do
              {
                tmp = toks;
                *buff++ = *string; // Assume it's correct and place it.
                do                 // I can't think of a faster way.
                {
                  if (*string == *tmp)
                  {
                    buff--;  // Not correct, pull back and move on.
                    break;
                  }
                }while (*++tmp);
              }while (*++string);
            
              *buff++ = '\0';  // Null terminate
            }
            
            int main()
            {
              char * string = "(555) 555-5555";
              char * toks = "()-";
              char buff[ strlen(string) + 1 ];
            
              cfilter(buff, string, toks);
              printf("%s\n", buff);
            
              return 0;
            }
            

            【讨论】:

            • 这不符合 OP 的要求;它也会删除空格。
            【解决方案12】:

            使用 std::wstringwchar_t(需要 Unicode 标头):

            //#include <tchar.h>
            std::wstring phone(L"(555) 555-5555");
            

            ...下一个花哨的静态范围初始化器;没有必要以完全相同的方式设置 badChars2。这太过分了;比其他任何东西都更学术:

            const wchar_t *tmp = L"()-"; 
            const std::set<wchar_t> badChars2(tmp,tmp + sizeof(tmp)-1);
            

            简单、简洁的 lambda:

            1. 在 lambda 捕获列表中使用 phone
            2. 使用Erase-remove idiom
            3. 手机中删除所有坏字符

              for_each(badChars2.begin(), badChars2.end(), [&phone](wchar_t n){
                   phone.erase(std::remove(phone.begin(), phone.end(), n), phone.end());
              });
              wcout << phone << endl;
              

            输出:“555 5555555”

            【讨论】:

              【解决方案13】:

              对于那些喜欢更简洁、更易于阅读的 lambda 编码风格的人...

              此示例从宽字符串中删除所有非字母数字字符和空格字符。您可以将它与任何其他 ctype.h 辅助函数混合使用,以删除看起来复杂的基于字符的测试。

              (我不确定这些函数将如何处理 CJK 语言,所以请轻点。)

                  // Boring C loops: 'for(int i=0;i<str.size();i++)' 
                  // Boring C++ eqivalent: 'for(iterator iter=c.begin; iter != c.end; ++iter)'
              

              看看你是否觉得这比嘈杂的 C/C++ for/iterator 循环更容易理解:

              TSTRING label = _T("1.   Replen & Move  RPMV");
              TSTRING newLabel = label;
              set<TCHAR> badChars; // Use ispunct, isalpha, isdigit, et.al. (lambda version, with capture list parameter(s) example; handiest thing since sliced bread)
              for_each(label.begin(), label.end(), [&badChars](TCHAR n){
                  if (!isalpha(n) && !isdigit(n))
                      badChars.insert(n);
              });
              
              for_each(badChars.begin(), badChars.end(), [&newLabel](TCHAR n){
                  newLabel.erase(std::remove(newLabel.begin(), newLabel.end(), n), newLabel.end());
              });
              

              运行此代码后的新标签结果:“1ReplenMoveRPMV

              这只是学术性的,因为将 lambda0(第一个 for_each)中的“if”逻辑组合到单个 lambda1(第二个 for_each),如果您已经确定了哪些字符是“badChars”。

              【讨论】:

              【解决方案14】:

              很多好的答案,这是清理一串数字的另一种方法,不是删除字符,而是将数字移出。

              string str("(555) 555-5555"), clean;
              for (char c : str)
                  if (c >= 48 and c <= 57)
                      clean.push_back(c);
              

              【讨论】:

                【解决方案15】:

                从 C++20 开始,您可以将 erase/erase_if 用于 std::basic_string,这基本上是擦除删除习惯用法的便捷包装

                std::erase(phone, '('); 
                

                std::erase_if(phone, [](char x) { 
                                        return x == '(' or x == ')' or x == '-'; 
                                     });
                

                请注意,这些函数还会返回已删除字符的计数。

                【讨论】:

                  猜你喜欢
                  • 2021-01-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-04-30
                  相关资源
                  最近更新 更多