C++删除字符串上的标点符号,擦除()/迭代器问题

C++ removing punctuation on strings, erase()/iterator issue

本文关键字:迭代器 问题 擦除 字符串 删除 标点符号 C++      更新时间:2023-10-16

我知道我不是第一个提出反向迭代器试图在字符串上调用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;