从字符串中删除最后一个字符的方法是使用remove_if函数。

5

我希望能够删除通过引用传递的字符串中的第一个和最后一个括号。不幸的是,我在有条件地删除第一个和最后一个元素方面遇到了困难。 我无法理解为什么使用迭代器时remove_if不能按照我的预期工作。

演示

#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

这个答案可能会有用(其他答案也可能适用)https://dev59.com/FXVC5IYBdhLWcg3wrDNd#25385766 - Galik
基本上,remove_if 不会改变容器的大小。相反,它返回新的结尾位置。您必须手动更新字符串以反映更改。在第一个字符上使用它可能会使字符串混乱不堪。 - kuroi neko
5个回答

6

如果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(因为它很可能是一个典型的指针)。 - Tomasz Lewowski
它可以消除未定义行为,但是你说得对,这并不能使你的代码正确运行——其他错误仍然存在。 - marcinj
@CronAcronis - 首先 str.erase 使用了错误的结束迭代器范围,现在应该可以工作了。 - marcinj
您还必须检查没有括号存在的情况,字符串长度为1时的情况... - marcinj

4
问题出在这里:
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)); 的情况下,它将删除所有括号。 - Cron Merdek

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 - Cron Merdek
@CronAcronis - 没错。在这里使用 remove_if 是错误的工具。 - Pete Becker

2

正如@PeteBecker在评论中所述,remove_if算法在这里并不合适。由于您只想删除第一个和最后一个字符(如果它们匹配),因此更简单的方法是测试back()front()是否与两个括号()(方括号将是[])相匹配。

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

这里

提供了一个实时的代码示例。

1
你所需要的只是这个:


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

实时演示

通过声明:

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

你触发了未定义的行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接