【问题标题】:remove_if last character from a stringremove_if 字符串中的最后一个字符
【发布时间】:2016-08-12 21:31:15
【问题描述】:

我想删除通过引用字符串传递的第一个和最后一个括号。不幸的是,我很难有条件地删除第一个和最后一个元素。 我不明白为什么 remove_if 不能像我期望的那样使用迭代器。

Demo

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

void print_wo_brackets(string& str){
    auto detect_bracket = [](char x){ return(')' == x || '(' == x);};
    if (!str.empty()) 
    {
        str.erase(std::remove_if(str.begin(), str.begin() + 1, detect_bracket));
    }
    if (!str.empty()) 
    {
        str.erase(std::remove_if(str.end()-1, str.end(), detect_bracket));
    }
}

int main()
{
    string str = "abc)";
    cout << str << endl;
    print_wo_brackets(str);
    cout << str << endl;


    string str2 = "(abc";
    cout << str2 << endl;
    print_wo_brackets(str2);
    cout << str2 << endl;

    return 0;
}

输出

abc)
ac    <- HERE I expect abc 
(abc 
abc

【问题讨论】:

  • 这个答案可能有用(其他答案也可能适合)stackoverflow.com/a/25385766/3807729
  • 基本上,remove_if 不会改变容器的大小。相反,它返回新的结束。您必须手动更新字符串以反映更改。在第一个字符上使用它可能会使字符串变得一团糟。

标签: c++ string algorithm c++11 stl


【解决方案1】:

如果 remove_if 返回 end 迭代器,那么您将尝试擦除不存在的元素。您应该在两个地方都使用erase 版本的范围:

void print_wo_brackets(string& str){
    auto detect_bracket = [](char x){ return(')' == x || '(' == x);};
    if (!str.empty())
    {
        str.erase(std::remove_if(str.begin(), str.begin() + 1, detect_bracket), str.begin() + 1);
    }
    if (!str.empty())
    {
        str.erase(std::remove_if(str.end()-1, str.end(), detect_bracket), str.end());
    }
}

【讨论】:

  • 这实际上是一个有效的答案,但在示例中end 指向字母b(因为它很可能是一个典型的指针)
  • 它删除了 UB 但是 - 你是对的,这不会让你的代码运行正确 - 其他错误仍然存​​在
  • @CronAcronis - 第一个 str.erase 使用了错误的结束迭代器范围,现在它应该可以工作了
  • 您还必须检查不存在括号的情况,字符串长度为 1,...
【解决方案2】:

问题出在这里:

if (!str.empty()) 
{
    str.erase(std::remove_if(str.begin(), str.begin() + 1, detect_bracket));
}

你无条件擦除。 std::remove_if 将迭代器返回到“删除”范围的开头。如果没有要删除的元素,则返回范围的结尾(在这种情况下为str.begin() + 1)。所以你删除了begin+1 元素,即b

为了避免这个问题,您可能不应该做更多类似的事情:

if (!str.empty()) 
{
    auto it = std::remove_if(str.begin(), str.begin() + 1, detect_bracket);
    if(it != str.begin() + 1)
        str.erase(it);
}

我假设您只是想检查标准库和迭代器的行为,否则检查:

if(str[0] == '(' || str[0] == ')')
    str.erase(0);

要简单得多。

【讨论】:

  • 但如果是str.erase(std::remove_if(str.begin(), str.end, detect_bracket));,它将删除所有括号。
【解决方案3】:

替代方案:

#include <iostream>
#include <string>

std::string without_brackets(std::string str, char beg = '(', char end = ')') {
    auto last = str.find_last_of(end);
    auto first = str.find_first_of(beg);

    if(last != std::string::npos) {
        str.erase(str.begin()+last);
    }
    if(first != std::string::npos) {
        str.erase(str.begin()+first);
    }

    return str;
}


using namespace std;

int main() {
    cout << without_brackets("abc)") << endl
         << without_brackets("(abc") << endl
         << without_brackets("(abc)") << endl
         << without_brackets("abc") << endl;
    return 0;
}

见:http://ideone.com/T2bZDe

结果:

abc
abc
abc
abc

【讨论】:

  • 不错的解决方案,但不适用于remove_if
  • @CronAcronis - 完全正确。 remove_if 在这里是错误的工具。
【解决方案4】:

正如@PeteBecker 在 cmets 中所述,remove_if 在这里不是正确的算法。由于您只想删除匹配的第一个和最后一个字符,因此更简单的方法是针对两个括号 () 测试 back()front()(括号将是 [ 和 @987654328 @)

void remove_surrounding(string& str, char left = '(', char right = ')')
{
    if (!str.empty() && str.front() == left)
        str.erase(str.begin());
    if (!str.empty() && str.back() == right)
        str.erase(str.end() - 1);
}

Live Example

【讨论】:

    【解决方案5】:

    你只需要这个:

    void print_wo_brackets(string& str){
      str.erase(std::remove_if(str.begin(), str.end(),
        [&](char &c) { return (c == ')' || c == '(') && (&c == str.data() || &c == (str.data() + str.size() - 1));}), str.end());
    }
    

    Live Demo

    通过说明:

    str.erase(std::remove_if(str.end()-1, str.end(), detect_bracket));
    

    你正在唤起未定义的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-04
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      • 2017-10-30
      相关资源
      最近更新 更多