Làm cách nào để xóa các ký tự nhất định khỏi một chuỗi trong C ++?


96

Ví dụ, tôi có một người dùng nhập số điện thoại.

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

Tôi muốn xóa các ký tự "(", ")" và "-" khỏi chuỗi. Tôi đã xem xét các hàm loại bỏ, tìm và thay thế chuỗi tuy nhiên tôi chỉ thấy rằng chúng hoạt động dựa trên vị trí.

Có một hàm chuỗi nào mà tôi có thể sử dụng để chuyển một ký tự "(" chẳng hạn, và nó có xóa tất cả các phiên bản trong một chuỗi không?

Câu trả lời:


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;

Để sử dụng như một chức năng :

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
Cái này hoạt động ra sao? Nó không phải là một phủ định kép để sử dụng xóa và loại bỏ? Đối với tôi, điều này đọc: "xóa các ký tự ở vị trí mà () - không." Và vì mỗi cái được thực hiện tại một thời điểm, nó không nên xóa TẤT CẢ các ký tự? Tôi đã đọc tài liệu về cả hai chức năng và điều này không có ý nghĩa gì đối với tôi. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent

@Brent std :: remove () sẽ KHÔNG xóa bất kỳ ký tự hợp lệ nào khỏi chuỗi, nó chỉ di chuyển các ký tự hợp lệ lại với nhau.
lk_vc

20
@Brent và các độc giả tương lai, đây là thành ngữ Xoá bỏ . Một cách ngắn gọn, std::removedi chuyển các mục không bị xóa lên phía trước của vectơ và trả về một trình vòng lặp trỏ ngay bên ngoài mục chưa được xóa cuối cùng. Sau đó, std::erasecắt vector từ trình vòng lặp đó đến cuối.
chwarr

1
Đối với thực sự C ++ phiên bản tôi nghĩ chúng ta nên sử dụng string chars("()-");và sau đó sử dụng .length()phương pháp để có được chiều dài và .at(i)phương pháp để truy cập chars :) fiddle Functionized - ideone.com/tAZt5I
jave.web

2
Để sử dụng như chức năng: ideone.com/XOROjq - công dụng<iostream> <algorithm> <cstring>
jave.web

36

Tôi muốn xóa các ký tự "(", ")" và "-" khỏi chuỗi.

Bạn có thể sử dụng std::remove_if()thuật toán để chỉ xóa các ký tự bạn chỉ định:

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

Các std::remove_if()thuật toán đòi hỏi một cái gì đó gọi là một vị, đó có thể là một con trỏ hàm như đoạn trên.

Bạn cũng có thể truyền một đối tượng hàm (một đối tượng làm quá tải ()toán tử gọi hàm ). Điều này cho phép chúng tôi tạo ra một giải pháp tổng quát hơn:

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

Bạn có thể chỉ định những ký tự cần loại bỏ với "()- "chuỗi. Trong ví dụ trên, tôi đã thêm một khoảng trắng để xóa các khoảng trắng cũng như dấu ngoặc đơn và dấu gạch ngang.


Bạn cũng có thể sử dụngispunct(int c)
MSalters

Thực hiện xuất sắc. Phương pháp này hoạt động hoàn hảo và có nhiều chỗ cho các động lực hơn nữa. Cảm ơn bạn đã phản hồi. MSalters, tôi cũng sẽ tra cứu hàm ispunct (int c) và báo cáo lại hoạt động của mình.
SD.

12

remove_if () đã được đề cập. Tuy nhiên, với C ++ 0x, bạn có thể chỉ định vị từ cho nó bằng lambda.

Dưới đây là một ví dụ về điều đó với 3 cách khác nhau để thực hiện lọc. phiên bản "sao chép" của các hàm cũng được bao gồm trong các trường hợp khi bạn đang làm việc với một const hoặc không muốn sửa đổi bản gốc.

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

Thay vì const char & c, tôi thực sự nên sử dụng const string :: value_type &. Nhưng, nó không phải là một vấn đề lớn trong trường hợp này.
Shadow2531

1
Đây là một thực hiện rất kỹ lưỡng. Tôi đánh giá cao nó và cũng sẽ sử dụng cách triển khai này.
SD.

8

Đây là một giải pháp khác nhau cho bất kỳ ai quan tâm. Nó sử dụng phạm vi For mới trong c ++ 11

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) str2khởi tạo không bắt buộc. (2) str = std::move(str2)sẽ hiệu quả hơn.
Ajay

6

Tôi e rằng không có thành viên nào như vậy cho std :: string, nhưng bạn có thể dễ dàng lập trình loại hàm đó. Nó có thể không phải là giải pháp nhanh nhất nhưng điều này sẽ đủ:

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

CHỈNH SỬA: Đọc câu trả lời bên dưới, tôi hiểu nó tổng quát hơn, không chỉ để phát hiện chữ số. Giải pháp trên sẽ bỏ qua mọi ký tự được truyền trong chuỗi đối số thứ hai. Ví dụ:

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

Sẽ cho kết quả

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

Lưu ý: Bạn cần viết ptr_fun<int, int>chứ không phải đơn giảnptr_fun


Làm thế nào đây không phải là câu trả lời được chọn?
user3240688

@ user3240688 Lưu ý rằng std :: ptr_fun không được chấp nhận trong C ++ 11 và sẽ bị xóa trong C ++ 17 và std :: not1 không được dùng trong C ++ 17. Bạn có thể sử dụng std::crefhoặc std::function(hoặc lambdas).
Roi Danton

3

Có, bạn có thể sử dụng hàm isdigit () để kiểm tra các chữ số :)

Của bạn đây:

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

Hy vọng nó giúp :)


Điều này có thể được sửa đổi để loại bỏ phần tử trả về false. Cảm ơn bạn.
SD.

3

boost::is_any_of

Dải cho tất cả các ký tự từ một chuỗi xuất hiện trong một chuỗi đã cho khác:

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

Đã thử nghiệm trong Ubuntu 16.04, Boost 1.58.


2

Nếu bạn có quyền truy cập vào trình biên dịch hỗ trợ các mẫu đa dạng, bạn có thể sử dụng điều này:

#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

Đây là một giải pháp thay thế khác:

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

Làm việc với std :: string và std :: wstring


1

Tôi là người mới, nhưng một số câu trả lời ở trên cực kỳ phức tạp, vì vậy đây là một giải pháp thay thế.

LƯU Ý: Miễn là 0-9 tiếp giáp (mà chúng phải là theo tiêu chuẩn), điều này sẽ lọc ra tất cả các ký tự khác trừ các số và ''. Biết 0-9 phải liền kề và một char thực sự là một int, chúng ta có thể làm như dưới đây.

CHỈNH SỬA: Tôi không nhận thấy áp phích cũng muốn có khoảng trống, vì vậy tôi đã thay đổi nó ...

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

Dưới đây là để lọc các ký tự được cung cấp.

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

Điều đó không làm những gì OP muốn; nó cũng xóa các khoảng trắng.
Andrew Barber

1

Sử dụng std :: wstringwchar_t (yêu cầu tiêu đề Unicode ):

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

... bộ khởi tạo phạm vi tĩnh lạ mắt tiếp theo; không cần thiết để thiết lập badChars2 theo cách này chính xác. Nó quá mức cần thiết; học thuật hơn bất cứ điều gì khác:

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

Lambda đơn giản, ngắn gọn:

  1. Sử dụng điện thoại trong danh sách chụp lambda.
  2. Sử dụng thành ngữ Erase-remove
  3. Xóa tất cả các ký tự xấu khỏi điện thoại

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

Đầu ra: "555 5555555"


1

Đối với những người bạn thích phong cách viết mã lambda ngắn gọn hơn, dễ đọc hơn ...

Ví dụ này xóa tất cả các ký tự không phải chữ và số và khoảng trắng khỏi một chuỗi rộng. Bạn có thể kết hợp nó với bất kỳ hàm trợ giúp nào khác của ctype.h để loại bỏ các bài kiểm tra dựa trên ký tự phức tạp.

(Tôi không chắc các chức năng này sẽ xử lý các ngôn ngữ CJK như thế nào, vì vậy hãy đi bộ nhẹ nhàng ở đó.)

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

Xem nếu bạn không thấy điều này dễ hiểu hơn các vòng lặp C / C ++ cho / iterator ồn ào:

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 kết quả sau khi chạy mã này: " 1ReplenMoveRPMV "

Điều này chỉ mang tính học thuật, vì rõ ràng sẽ chính xác, ngắn gọn và hiệu quả hơn nếu kết hợp logic 'if' từ lambda0 ( for_each đầu tiên ) vào lambda1 ( for_each thứ hai ), nếu bạn đã thiết lập các ký tự nào là "badChars" .


Ghi nhận câu trả lời của @Eric Z vì đã đề cập và sử dụng thành ngữ Xoá bỏ tiện dụng. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

Rất nhiều câu trả lời hay, đây là một cách khác để xóa một chuỗi số, không phải là xóa các ký tự mà bằng cách di chuyển các số ra ngoài.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.