Як видалити всі випадки символу в рядку C ++


98

Я використовую наступне:

replace (str1.begin(), str1.end(), 'a' , '')

Але це дає помилку компіляції.


8
''насправді не є персонажем.
п. 'займенники' m.

4
Ну, це, безумовно, допомогло б знати про помилку, яку ви отримуєте.
SBI

3
будь ласка, є багато контекстів, де замінити - це відповідна думка, тільки не ця.
RichardPlunkett

2
можливий дублікат Як видалити символи з рядка
jogojapan

Відповіді:


171

В основному, replaceзамінює персонажа іншим і ''не є персонажем. Те, що ви шукаєте erase.

Дивіться це запитання, яке відповідає на ту саму проблему. У вашому випадку:

#include <algorithm>
str.erase(std::remove(str.begin(), str.end(), 'a'), str.end());

Або скористайтеся, boostякщо це варіант для вас, наприклад:

#include <boost/algorithm/string.hpp>
boost::erase_all(str, "a");

Все це добре задокументовано на довідкових веб-сайтах . Але якщо ви не знали про ці функції, ви могли б легко робити такі речі вручну:

std::string output;
output.reserve(str.size()); // optional, avoids buffer reallocations in the loop
for(size_t i = 0; i < str.size(); ++i)
  if(str[i] != 'a') output += str[i];

2
Хіба не наданий вами алгоритм O(n^2)?
jww

@jww: Я припускаю, що ви говорите про останній зразок коду і nце оригінальна довжина рядка. Для кожного введеного символу я роблю 1 тест на O(1)символи та додаю 0 або 1 символ. Додавання символу - O(1)це достатня кількість пам’яті, зарезервована, або O(current_length)якщо виділено новий буфер. Якщо ви робите це output.reserve(str.size())до циклу, цього ніколи не трапляється, і у вас є загальна O(n)вартість. В іншому випадку асимптотично, я думаю, ціна O(n . log(n) )пов’язана із стратегією перерозподілу контейнерів STL.
Антуан

5
Мені потрібно було #include <algorithm>
S Meaden

Приємна відповідь. Завжди добре, якщо відповідь містить багато рішень. Для мене forнайбільш підходящим є рішення із .
Дмитро

@DmitryNichiporenko відповідь із за не може бути найбільш підходящою. Якщо у вас є предикат або не пустий результат, я б вважав за краще: output.reserve (str.size () + output.size ()); std :: copy_if (str.begin (), str.end (), std :: back_inserter (output), [] (char c) {повернути предикат (c);});
jimifiki

10

Алгоритм std::replaceпрацює для кожного елемента в заданій послідовності (тому він замінює елементи різними елементами і не може замінити його нічим ). Але порожнього персонажа немає . Якщо ви хочете видалити елементи з послідовності, наступні елементи потрібно перемістити , і std::replaceце не працює так.

Ви можете спробувати використовувати std::remove( разом зstd::erase ) для досягнення цього.

str.erase(std::remove(str.begin(), str.end(), 'a'), str.end());

8

Використання copy_if:

#include <string>
#include <iostream>
#include <algorithm>
int main() {
    std::string s1 = "a1a2b3c4a5";
    char s2[256];
    std::copy_if(s1.begin(), s1.end(), s2, [](char c){return c!='a';});
    std::cout << s2 << std::endl;
    return 0;
}

3
string RemoveChar(string str, char c) 
{
   string result;
   for (size_t i = 0; i < str.size(); i++) 
   {
          char currentChar = str[i];
          if (currentChar != c)
              result += currentChar;
   }
       return result;
}

Ось як я це зробив.

Або ви можете зробити те, що згадав Антуан:

Дивіться це запитання, яке відповідає на ту саму проблему. У вашому випадку:

#include <algorithm>
str.erase(std::remove(str.begin(), str.end(), 'a'), str.end());

1

Цей код усуває повторення символів, тобто, якщо вхідний сигнал aaabbcc, тоді вихід буде abc.

cin >> s;
ans = "";
ans += s[0];
for(int i = 1;i < s.length();++i)
if(s[i] != s[i-1])
    ans += s[i];
cout << ans << endl;

1

Якщо у вас є predicateта / або не пусте місце outputдля заповнення відфільтрованим рядком, я б розглянув:

output.reserve(str.size() + output.size());  
std::copy_if(str.cbegin(), 
             str.cend(), 
             std::back_inserter(output), 
             predicate});

У вихідному питанні присудок є [](char c){return c != 'a';}


0

Виходячи з інших відповідей, ось ще один приклад, коли я видалив усі спеціальні символи в даному рядку:

#include <iostream>
#include <string>
#include <algorithm>

std::string chars(".,?!.:;_,!'\"-");

int main(int argc, char const *argv){

  std::string input("oi?");
  std::string output = eraseSpecialChars(input);   

 return 0;
}




std::string eraseSpecialChars(std::string str){

std::string newStr;
    newStr.assign(str);  

    for(int i = 0; i < str.length(); i++){
        for(int  j = 0; j < chars.length(); j++ ){
            if(str.at(i) == chars.at(j)){
                char c = str.at(i);
                newStr.erase(std::remove(newStr.begin(), newStr.end(), c), newStr.end());
            }
        }

    }      

return newStr; 
}

Вхід проти виходу:

Input:ra,..pha
Output:rapha

Input:ovo,
Output:ovo

Input:a.vo
Output:avo

Input:oi?
Output:oi

-1

Я здогадуюсь, що метод std: remove працює, але це дало певну проблему сумісності з включеннями, тому я закінчив писати цю маленьку функцію:

string removeCharsFromString(const string str, char* charsToRemove )
{
    char c[str.length()+1]; // + terminating char
    const char *p = str.c_str();
    unsigned int z=0, size = str.length();
    unsigned int x;
    bool rem=false;

    for(x=0; x<size; x++)
    {
        rem = false;
        for (unsigned int i = 0; charsToRemove[i] != 0; i++)
        {
            if (charsToRemove[i] == p[x])
            {
                rem = true;
                break;
            }
        }
        if (rem == false) c[z++] = p[x];
    }

    c[z] = '\0';
    return string(c);
}

Просто використовуйте як

myString = removeCharsFromString (myString, "abc \ r");

і це видалить усі випадки подання цього списку символів.

Це також може бути трохи ефективнішим, оскільки цикл повертається після першого збігу, тому насправді ми робимо менше порівняння.


1
Ви правильно вгадали. Замість того, щоб писати власні, краще з’ясуйте, чому ви не можете використовувати стандартні заголовки на C ++.
xtofl

Ну, це особиста думка xtofl, Не завжди добре використовувати третій код, який ви насправді не знаєте, що він робить, чи виступи, а не писати те, що вам конкретно потрібно.
Damien

1
Я розумію, що ти маєш на увазі. Хоча саме смиренність змушує мене вибирати версію, яку перевірили, протестували та оптимізували професійні автори бібліотек, а не моя. Стандартної бібліотеки можна вважати необхідними знаннями: його функції, а також його виконання складності.
xtofl

Якщо не брати до уваги рядки, це рішення C на C ++. Я не думаю, що це слід було проголосувати проти.
Сова

-1

Ось як я це роблю:

std::string removeAll(std::string str, char c) {
    size_t offset = 0;
    size_t size = str.size();

    size_t i = 0;
    while (i < size - offset) {
        if (str[i + offset] == c) {
            offset++;
        }

        if (offset != 0) {
            str[i] = str[i + offset];
        }

        i++;
    }

    str.resize(size - offset);
    return str;
}

В основному, коли я знаходжу певний символ, я переміщую зміщення і переміщую його до правильного індексу. Я не знаю, чи це правильно чи ефективно, я починаю (ще раз) на C ++, і я був би вдячний за будь-який вступ щодо цього.


4
Повертаючись до цього питання через 4 місяці, я справді не знаю, чому я не використовував std :: erase або std :: replace.
Рікардо Піпер

-3
#include <string>
#include <algorithm>
std::string str = "YourString";
char chars[] = {'Y', 'S'};
str.erase (std::remove(str.begin(), str.end(), chars[i]), str.end());

Видалить великі букви Y та S з вулиці, залишивши "ourtring".

Зверніть увагу, що removeце алгоритм і потребує <algorithm>включення заголовка .


так, я думаю, що мається на увазі цикл над символами масиву hte, які він залишив
позаду
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.