std::string 擦除最后一个字符失败?

Posted

技术标签:

【中文标题】std::string 擦除最后一个字符失败?【英文标题】:std::string erase last character fails? 【发布时间】:2008-10-23 19:38:13 【问题描述】:

我正在尝试将通配符形式 ("*word*") 的用户输入更改为正则表达式格式。为此,我使用下面的代码在输入的开头和结尾去掉'*',以便我可以在任一端添加正则表达式字符:

string::iterator    iter_begin = expressionBuilder.begin();
string::iterator    iter_end = expressionBuilder.end();
iter_end--;
if ((char)*iter_begin == '*' && (char)*iter_end == '*')

    expressionBuilder.erase(iter_begin);
    expressionBuilder.erase(iter_end);
    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b";

但是,对"expressionBuilder.erase(iter_end)" 的调用确实不会从输入字符串中删除尾随'*',因此我得到了一个不正确的正则表达式。我在这里做错了什么? "(char)*iter_end == '*'" 必须为 true 才能运行 if 语句中的代码(它确实如此),那么为什么传递给 erase() 时相同的迭代器不能工作?

【问题讨论】:

【参考方案1】:

到目前为止,您的原始代码和建议的解决方案除了您发布的明显问题外还有几个问题:

在修改字符串后使用无效的迭代器 即使在字符串被修改之前也取消引用可能无效的迭代器(例如,如果字符串为空) 如果 expressionBuilder 字符串仅包含单个“*”字符时出现错误

现在,如果使用 sn-p/routine 的代码已经在验证字符串是否包含至少 2 个字符,那么最后两项可能不是真正的问题,但如果不是这种情况,我相信以下内容面对 expressionBuilder 的任意值时更加健壮:

// using the reverse iterator rbegin() is a nice easy way 
//     to get the last character of a string

if ( (expressionBuilder.size() >= 2) &&
    (*expressionBuilder.begin()  == '*') &&
    (*expressionBuilder.rbegin() == '*') ) 

    expressionBuilder.erase(expressionBuilder.begin());

    // can't nicely use rbegin() here because erase() wont take a reverse
    //  iterator, and converting reverse iterators to regular iterators
    //  results in rather ugly, non-intuitive code
    expressionBuilder.erase(expressionBuilder.end() - 1); // note - not invalid since we're getting it anew

    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b";

请注意,当expressionBuilder"""*""**" 时,此代码将起作用,因为它不会执行任何未定义的操作。但是,在这些情况下,它可能不会产生您想要的结果(那是因为我不知道在这些情况下您到底想要什么)。修改以满足您的需求。

【讨论】:

谢谢。在这一点上,我几乎知道字符串不是空的或“*”,但我同意这样编码会更好,以防万一以后发生变化。【参考方案2】:

尝试以相反的顺序擦除它们:

expressionBuilder.erase(iter_end);
expressionBuilder.erase(iter_begin);

删除第一个 * 后,iter_end 指的是示例中字符串末尾之后的一个字符。 STL documentation 表示迭代器被 erase() 无效,所以从技术上讲,我的示例也是错误的,但我相信它会在实践中起作用。

【讨论】:

幸运的是,字符串不需要使用迭代器,大多数函数都有一个采用索引的形式。尽管如此,就像你说的那样,即使有索引擦除,它仍然应该“从后到前”完成。 P4tXrx5jrMlbhyludk9pxHBT30kGHo9n:关于 end(),你是对的,但有一个 iter_end-- 在那里查看字符串的实际最后一个字符。 这很有意义,颠倒顺序确实解决了问题。谢谢! 对不起,我错过了“--”这一行。 @Greg:其他一些帖子得出结论,迭代器之后被擦除的迭代器无效。 (***.com/questions/62340/…)【参考方案3】:

(已修改,因为我错过了iter_end-- 行)。

您可能需要一个 if 语句,它只检查 *iter_begin == '*',然后调用 find() 来获取另一个 '*'。或者您可以使用rbegin() 来获取“反向序列的开始迭代器”,将其推进一个,然后调用base() 将其转换为常规迭代器。这将使您获得序列中的最后一个字符。


更好的是,std::stringrfind() and find_last_of() methods。他们会给你最后一个'*'。你也可以简单地调用replace(),而不是去掉'*'s,然后重新添加新的东西。

【讨论】:

请注意有一个 iter_end-- 在其中备份一个字符。 你是否错过了“iter_end--;”行,它将迭代器移回最后一项?我确信 Greg 的回答是对的,因为字符串迭代器基本上只是索引,所以结束索引被第一次擦除无效。 我试图避免“find_last_of”,因为我已经知道角色在哪里,但可能是我想多了。【参考方案4】:

减去错误处理,您可以这样做:

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

string stripStar(const string& s) 
    return string(s.begin() + 1, s.end() - 1);


int main() 
   cout << stripStar("*word*") << "\n";

【讨论】:

如果你打电话给stripStar("word")甚至stripStar("word*")怎么办?我认为 OP 想要这种多功能性。

以上是关于std::string 擦除最后一个字符失败?的主要内容,如果未能解决你的问题,请参考以下文章

删除 std::string_view 的最后一个字符

在 std::string 末尾附近插入换行符 [重复]

从 std::string 解析整数,但如果是浮点数则失败

C++ std::string::find_last_of()函数(在字符串中搜索与参数中指定的任何字符匹配的最后一个字符)(从后往前找)(文件路径中找文件名,/\兼容windows和linux)

C++ std::string::find_last_of()函数(在字符串中搜索与参数中指定的任何字符匹配的最后一个字符)(从后往前找)(文件路径中找文件名,/\兼容windows和linux)

为什么我擦除其他字符时最后一个字符加倍,并且如何防止呢?