如何从C ++中的字符串中删除某些字符?


96

例如,我有一个用户输入电话号码。

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

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

我是否可以使用字符串函数来传递字符“(”,并删除字符串中的所有实例?

Answers:


140
   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, "()-" );

4
这是如何运作的?使用擦除和删除不是双重否定的吗?对我来说,它的意思是:“擦除()-不在的位置的字符。” 而且由于每个操作都是一次完成的,因此不应该删除所有字符吗?我已经阅读了这两个函数的文档,这对我来说毫无意义。cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent

@Brent std :: remove()不会从字符串中删除任何有效字符,它只是将有效字符一起移动。
lk_vc 2013年

20
@Brent和未来的读者,这是Erase-remove惯用语。简要地说,std::remove将未删除的项目移到向量的前面,并返回一个指向刚好在最后一个未删除的项目之后的迭代器。然后std::erase将向量从该迭代器修剪到末尾。
chwarr

1
对于真正的C ++版本,我认为我们应该使用string chars("()-");,然后使用.length()method来获取长度和.at(i)访问char的方法:)功能化的提琴-ideone.com/tAZt5I
jave.web

2
用作功能: ideone.com/XOROjq-使用<iostream> <algorithm> <cstring>
jave.web

36

我想从字符串中删除“(”,“)”和“-”字符。

您可以使用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()算法需要一个称为谓词的东西,它可以是一个函数指针,例如上面的代码片段。

您还可以传递函数对象(使函数调用()运算符重载的对象)。这使我们可以创建一个更通用的解决方案:

#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,2011年

优秀的实施。这种方法完美无瑕,并且有很大的空间可以进行进一步的动态处理。感谢您的答复。MSalters,我还将查找ispunct(int c)函数并报告我的工作情况。
SD。

12

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 char&c,我应该真正使用const string :: value_type&。但是,在这种情况下这没什么大不了的。
Shadow2531

1
这是一个非常彻底的实现。我对此表示赞赏,并将同样使用此实现。
SD。

8

对于有兴趣的人来说,这是一个不同的解决方案。它使用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
(1)str2不需要初始化。(2)str = std::move(str2)会更有效率。
Ajay

6

恐怕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

3
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<int, int>而不是简单ptr_fun


这不是所选答案吗?
user3240688 '17

@ user3240688请注意,std :: ptr_fun在C ++ 11中已弃用,在C ++ 17中将被删除,而std :: not1在C ++ 17中已弃用。您可以使用std::crefstd::function(或lambdas)。
罗伊·丹顿

3

是的,您可以使用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的元素。谢谢。
SD。

3

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中测试。


2

如果可以访问支持可变参数模板的编译器,则可以使用以下命令:

#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;
}

1

这是另一种选择:

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一起使用


1

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

注意:只要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的要求;它也会删除空格。
安德鲁·巴伯

1

使用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捕获列表中使用电话
  2. 使用擦除删除习惯用法
  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”


1

对于那些更喜欢简洁,易于阅读的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());
});

运行以下代码后,newLabel结果:“ 1ReplenMoveRPMV

这只是学术上的事情,因为如果您已经确定了哪些字符是“ badChars”,那么将lambda0(第一个for_each)的“ if”逻辑组合到单个lambda1(第二个for_each)显然会更加精确,简洁和高效。。


感谢@Eric Z提到并使用方便的Erase-remove习语的答案。en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

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

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.