我知道我不是第一个提出反向迭代器试图在字符串上调用erase()方法的人。但是,我找不到任何解决此问题的好方法。
我正在阅读一个文件的内容,其中包含一堆单词。当我读到一个单词时,我想把它传递给一个我称之为stripPunct的函数。但是,我只想在字符串的开头和结尾去除标点符号,而不是中间。
例如:
(单词)应去除"("和")",结果为单词
不要!应该去掉"!"导致只是不要
所以我的逻辑(我相信可以改进)是有两个 while 循环,一个从末尾开始,一个从开头开始,遍历和擦除,直到它到达非标点符号字符。
void stripPunct(string & str) {
string::iterator itr1 = str.begin();
string::reverse_iterator itr2 = str.rbegin();
while ( ispunct(*itr1) ) {
str.erase(itr1);
itr1++;
}
while ( ispunct(*itr2) ) {
str.erase(itr2);
itr2--;
}
}
但是,显然它不起作用,因为 erase() 需要一个常规迭代器而不是reverse_iterator。但无论如何,我觉得这种逻辑效率很低。
另外,我尝试仅使用常规迭代器代替reverse_iterator,从str.end()开始,然后递减它,但是它说如果我在str.end()启动它,我无法取消引用迭代器。
谁能帮我找到一个好方法?或者也许指出我已经拥有的解决方法?
提前非常感谢!
------------------ [ 编辑 ] ----------------------------
找到了一个解决方案,尽管它可能不是最好的解决方案:
// Call the stripPunct method:
stripPunct(str);
if ( !str.empty() ) { // make sure string is still valid
// perform other code
}
这是 stripPunct 方法:
void stripPunct(string & str) {
string::iterator itr1 = str.begin();
string::iterator itr2 = str.end();
while ( !(str.empty()) && ispunct(*itr1) )
itr1 = str.erase(itr1);
itr2--;
if ( itr2 != str.begin() ) {
while ( !(str.empty()) && ispunct(*itr2) ) {
itr2 = str.erase(itr2);
itr2--;
}
}
}
首先,请注意代码的几个问题:
- 使用
itr1
调用erase()
后,您已使itr2
无效。 - 当使用
reverse_iterator
向后浏览序列时,您希望使用++
,而不是--
(这就是反向迭代器存在的原因)。
现在,为了改进逻辑,您可以通过找到第一个您不想擦除的角色来避免单独擦除每个字符,并擦除到那时的所有内容。 find_if()
可用于帮助:
int not_punct(char c) {
return !ispunct((unsigned char) c);
}
void stripPunct(string & str) {
string::iterator itr = find_if( str.begin(), str.end(), not_punct);
str.erase( str.begin(), itr);
string::reverse_iterator ritr = find_if( str.rbegin(), str.rend(), not_punct);
str.erase( ritr.base(), str.end());
}
请注意,我使用 base()
来获取与reverse_iterator
对应的"常规"迭代器。 我发现是否需要调整base()
逻辑令人困惑(反向迭代器通常会让我感到困惑)——在这种情况下,它不是因为我们碰巧想在找到的角色之后开始擦除。
http://drdobbs.com/cpp/184401406 斯科特·迈耶斯(Scott Meyers)的这篇文章很好地处理了本节中的reverse_iterator::base()
。"准则 3:了解如何使用reverse_iterator的基础迭代器"。 该文章中的信息也被纳入了Meyer的"Effective STL"一书中。
你不能取消引用迭代器::end(),因为它指向无效内存(数组末尾之后的内存),所以你必须首先递减它。
最后一点:如果单词仅由标点符号组成,您的程序将失败,请务必处理。
如果你不介意负逻辑,你可以执行以下操作:
string tmp_str="";
tmp_str.reserve(str.length());
for (string::iterator itr1 = str.begin(); itr1 != str.end(); itr1++)
{
if (!ispunct(*itr1))
{
tmp_str.push_back(*itr1);
}
}
str = tmp_str;