จะลบอักขระบางตัวออกจากสตริงใน C ++ ได้อย่างไร


96

ตัวอย่างเช่นฉันให้ผู้ใช้ป้อนหมายเลขโทรศัพท์

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

ฉันต้องการลบอักขระ "(", ")" และ "-" ออกจากสตริง ฉันได้ดูที่สตริงลบค้นหาและแทนที่ฟังก์ชันแล้ว แต่ฉันเห็นว่ามันทำงานตามตำแหน่งเท่านั้น

มีฟังก์ชันสตริงที่ฉันสามารถใช้เพื่อส่งผ่านอักขระตัวอย่างเช่น "(" และได้ลบอินสแตนซ์ทั้งหมดภายในสตริงหรือไม่

คำตอบ:


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

21
@Brent และผู้อ่านในอนาคตนี้เป็นสำนวนลบลบ สั้น ๆstd::removeย้ายรายการที่ไม่ถูกลบไปที่ด้านหน้าของเวกเตอร์และส่งกลับตัวทำซ้ำที่ชี้เลยรายการสุดท้ายที่ยังไม่ถูกลบออก จากนั้นstd::eraseจดจ้องเวกเตอร์จากตัววนซ้ำนั้นไปยังจุดสิ้นสุด
chwarr

1
สำหรับเวอร์ชัน C ++ จริงๆฉันคิดว่าเราควรใช้string chars("()-");แล้วใช้.length()method เพื่อรับความยาวและ.at(i)วิธีการเข้าถึงตัวอักษร :) Functionized fiddle - 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

การใช้งานที่ยอดเยี่ยม วิธีนี้ทำงานได้อย่างไม่มีที่ติและมีช่องว่างมากมายสำหรับพลวัตเพิ่มเติม ขอบคุณสำหรับการตอบรับ MSalters ฉันจะค้นหาฟังก์ชัน ispunct (int c) และรายงานการทำงานของฉัน
SD.

12

มีการกล่าวถึง remove_if () แล้ว แต่ด้วย C ++ 0x คุณสามารถระบุเพรดิเคตสำหรับมันด้วยแลมด้าแทน

ด้านล่างนี้คือตัวอย่างที่มี 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

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

@ user3240688 โปรดทราบว่าstd :: ptr_funเลิกใช้งานใน C ++ 11 และจะถูกลบออกใน C ++ 17 และstd :: not1เลิกใช้งานใน C ++ 17 คุณสามารถใช้std::crefหรือstd::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;   
}

หวังว่าจะช่วยได้ :)


สิ่งนี้สามารถแก้ไขได้เพื่อลบองค์ประกอบที่ส่งคืนเท็จ ขอบคุณ.
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 ต้องการ; มันลบช่องว่างด้วย
Andrew Barber

1

ใช้std :: wstringและwchar_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);

แลมด้าที่เรียบง่ายและกระชับ:

  1. ใช้โทรศัพท์ในรายการจับแลมบ์ดา
  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

สำหรับพวกคุณที่ชอบรูปแบบการเข้ารหัสแลมบ์ดาที่กระชับและอ่านง่ายขึ้น ...

ตัวอย่างนี้ลบอักขระที่ไม่ใช่ตัวเลขและตัวอักษรและเว้นวรรคสีขาวทั้งหมดออกจากสตริงแบบกว้าง คุณสามารถผสมกับ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 ++ ที่มีเสียงดังสำหรับ / 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 "

นี่เป็นเพียงเชิงวิชาการเนื่องจากจะต้องมีความแม่นยำรัดกุมและมีประสิทธิภาพมากขึ้นในการรวมตรรกะ 'if' จาก lambda0 ( for_each ตัวแรก) ลงใน lambda1 (ตัวที่สองfor_each ) หากคุณได้กำหนดอักขระที่เป็น "badChars" แล้ว .


ให้เครดิตกับคำตอบของ @Eric Z สำหรับการกล่าวถึงและใช้สำนวนการลบ - ลบที่มีประโยชน์ 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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.