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

时间:2011-05-14 21:35:47

标签: c++ string iterator

我知道我不是第一个提出反向迭代器试图在字符串上调用erase()方法的人。但是,我无法找到任何好方法。

我正在读取文件的内容,其中包含一堆单词。当我读到一个单词时,我想将它传递给我称为stripPunct的函数。但是,我只想在字符串的开头和结尾处去除标点符号,而不是在中间。

例如:

(word)应该删除'('和')',结果只是 word

不要!应该删除'!'导致只是

所以我的逻辑(我肯定可以改进)是有两个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()需要常规迭代器而不是rev​​erse_iterator。但无论如何,我觉得这种逻辑效率很低。

另外,我尝试使用一个常规迭代器而不是一个reverse_iterator,在str.end()处启动它,然后递减它,但它说如果我在str.end()启动它就不能取消引用迭代器。 / p>

任何人都可以帮我一个好方法吗?或者可能指出我已有的解决方法?

提前非常感谢你!

------------------ [编辑] -------------------------- -

找到了解决方案,虽然它可能不是最好的解决方案:

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

3 个答案:

答案 0 :(得分:4)

首先,请注意代码中的几个问题:

  • 使用erase()致电itr1后,您已将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()是否需要调整混乱的逻辑(反向迭代器一般让我感到困惑) - 在这种情况下它不会因为我们碰巧想要在找到的字符后开始擦除。

Scott Meyers撰写的这篇文章http://drdobbs.com/cpp/184401406在本节中对reverse_iterator::base()进行了很好的处理。 “准则3:了解如何使用reverse_iterator的基础迭代器”。该文章中的信息也已纳入迈耶的“有效STL”一书中。

答案 1 :(得分:0)

你不能解除引用iterator :: end()因为它指向无效的内存(数组结束后的内存),所以你必须先减少它。

最后一点说明:如果这个单词只包含标点符号,那么你的程序就会失败,请务必处理。

答案 2 :(得分:0)

如果您不介意否定逻辑,则可以执行以下操作:

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;