Как удалить определенные символы из строки в C ?

#c #string #character

#c #строка #символ

Вопрос:

Например, у меня есть пользователь, вводящий номер телефона.

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

Я хочу удалить символы «(«, «)» и «-» из строки. Я просмотрел функции удаления, поиска и замены строк, однако я вижу только, что они работают на основе позиции.

Существует ли строковая функция, которую я могу использовать для передачи символа «(«например, и чтобы она удаляла все экземпляры в строке?

Ответ №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 amp;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, "()-" );
  

Комментарии:

1.Как это работает? Разве использование erase и remove не является двойным отрицанием? Для меня это звучит так: «сотрите символы, которые находятся в позициях, где () — нет». И поскольку каждый из них выполняется одновременно, не следует ли удалить ВСЕ символы? Я прочитал документацию по обеим функциям, и для меня это не имеет смысла. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase

2. @Brent и будущие читатели, это идиома Erase-удалить . Вкратце, std::remove перемещает не удаленные элементы в начало вектора и возвращает итератор, указывающий сразу за последним не удаленным элементом. Затем std::erase обрезает вектор из этого итератора до конца.

3. Я думаю, что для действительно версии C мы должны использовать string chars("()-"); , а затем использовать .length() метод для получения длины и .at(i) метод для доступа к символам 🙂 Функциональная скрипка — ideone.com/tAZt5I

4.Для использования в качестве функции: ideone.com/XOROjq — использует <iostream> <algorithm> <cstring>

5. вам лучше кэшировать, 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(), amp;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
}
  

Вы можете указать, какие символы удалять с помощью "()- " строки. В приведенном выше примере я добавил пробел, чтобы пробелы были удалены так же, как круглые скобки и тире.

Комментарии:

1. Вы также могли бы использовать ispunct(int c)

2. Отличная реализация. Этот метод работал безупречно и имеет много возможностей для дальнейшей динамики. Спасибо за ответ. Кроме того, я также посмотрю функцию ispunct (int c) и отчитаюсь о своей работе.

Ответ №3:

remove_if() уже упоминалось. Но в C 0x вы можете указать для нее предикат с помощью лямбда-выражения.

Ниже приведен пример этого с 3 различными способами выполнения фильтрации. «копирующие» версии функций также включены для случаев, когда вы работаете с константой или не хотите изменять оригинал.

 #include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;

stringamp; remove_chars(stringamp; s, const stringamp; chars) {
    s.erase(remove_if(s.begin(), s.end(), [amp;chars](const charamp; c) {
        return chars.find(c) != string::npos;
    }), s.end());
    return s;
}
string remove_chars_copy(string s, const stringamp; chars) {
    return remove_chars(s, chars);
}

stringamp; remove_nondigit(stringamp; s) {
    s.erase(remove_if(s.begin(), s.end(), [](const charamp; c) {
        return !isdigit(c);
    }), s.end());
    return s;
}
string remove_nondigit_copy(string s) {
    return remove_nondigit(s);
}

stringamp; remove_chars_if_not(stringamp; s, const stringamp; allowed) {
    s.erase(remove_if(s.begin(), s.end(), [amp;allowed](const charamp; c) {
        return allowed.find(c) == string::npos;
    }), s.end());
    return s;
}
string remove_chars_if_not_copy(string s, const stringamp; 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;
}
  

Комментарии:

1. Вместо const char amp; c мне действительно следовало использовать const string::value_typeamp; . Но в данном случае это не имеет большого значения.

2. Это очень тщательная реализация. Я ценю это и также буду использовать эту реализацию.

Ответ №4:

Вот другое решение для всех, кто заинтересован. Он использует новый диапазон For в 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) было бы более эффективно.

Ответ №5:

Боюсь, что такого элемента для std::string не существует, но вы можете легко запрограммировать такого рода функции. Возможно, это не самое быстрое решение, но этого было бы достаточно:

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

РЕДАКТИРОВАТЬ: Прочитав приведенный ниже ответ, я понял, что он является более общим, а не только для определения digit. В приведенном выше решении будут опущены все символы, переданные во второй строке аргумента.
Например:

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

Приведет к

 99999876543.87
  

Ответ №6:

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.

Ответ №7:

 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

Комментарии:

1. почему это не выбранный ответ?

2. @user3240688 Обратите внимание, что std::ptr_fun не рекомендуется в C 11 и будет удален в C 17, а std::not1 не рекомендуется в C 17. Вы могли бы использовать std::cref or std::function (или лямбды).

Ответ №8:

Да, вы можете использовать функцию 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;   
}
  

Надеюсь, это поможет 🙂

Комментарии:

1. Это можно изменить, чтобы удалить элемент, который возвращает false . Спасибо.

Ответ №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 amp; str) {
    str.erase(std::remove_if(str.begin(), str.end(), amp;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> amp; 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' amp;amp; *string <= '9') || *string == ' ')
      *buff   = *string;
  } while ( *  string );
  *buff   = ''; // Null terminate
}

int main()
{
  const char *string = "(555) 555-5555";
  char buff[ strlen(string)   1 ];

  numfilter(buff, string);
  printf("%sn", 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   = '';  // Null terminate
}

int main()
{
  char * string = "(555) 555-5555";
  char * toks = "()-";
  char buff[ strlen(string)   1 ];

  cfilter(buff, string, toks);
  printf("%sn", buff);

  return 0;
}
  

Комментарии:

1. Это не делает того, чего хотел OP; это также удаляет пробелы.

Ответ №12:

Используя 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. Использует phone в списке захвата лямбда.
  2. Использует идиому Erase-удалить
  3. Удаляет все неправильные символы с телефона

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

Вывод: «555 5555555»

Ответ №13:

Для тех из вас, кто предпочитает более лаконичный, более удобный для чтения стиль кодирования на языке лямбда…

В этом примере удаляются все не буквенно-цифровые символы и пробелы из широкой строки. Вы можете смешать его с любым другим 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 amp; 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(), [amp;badChars](TCHAR n){
    if (!isalpha(n) amp;amp; !isdigit(n))
        badChars.insert(n);
});

for_each(badChars.begin(), badChars.end(), [amp;newLabel](TCHAR n){
    newLabel.erase(std::remove(newLabel.begin(), newLabel.end(), n), newLabel.end());
});
  

После выполнения этого кода появляется новая метка: «1ReplenMoveRPMV«

Это чисто академический вопрос, поскольку было бы более точным, кратким и эффективным объединить логику ‘if’ из lambda0 (первый для каждого) в один lambda1 (второй для каждого), если вы уже установили, какие символы являются «плохими символами».

Комментарии:

1. Спасибо @Eric Z’s answer за упоминание и использование удобной идиомы Erase-удалить. en.wikipedia.org/wiki/Erase-remove_idiom

Ответ №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 , который в основном является удобной оболочкой для идиомы erase-remove

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

и

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

Обратите внимание, что эти функции также возвращают количество удаленных символов.