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

C++ C++;删除字符串上的标点符号,擦除()/迭代器问题,c++,string,iterator,C++,String,Iterator,我知道我不是第一个提出反向迭代器试图调用字符串上的erase()方法的问题的人。然而,我找不到任何好办法来解决这个问题 我正在读一个文件的内容,其中包含一堆单词。当我读入一个单词时,我想把它传递给一个我称为stripPunct的函数。但是,我只想在字符串的开头和结尾删除标点符号,而不是在中间。 例如: (单词)应该去掉“(‘and’”,从而得到一个单词 不要!应该脱掉结果就是不 所以我的逻辑(我相信可以改进)是有两个while循环,一个从末尾开始,一个从开头开始,遍历和擦除,直到碰到一个非标点字

我知道我不是第一个提出反向迭代器试图调用字符串上的erase()方法的问题的人。然而,我找不到任何好办法来解决这个问题

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

例如:

(单词)应该去掉“(‘and’”,从而得到一个单词

不要!应该脱掉结果就是不

所以我的逻辑(我相信可以改进)是有两个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()需要一个常规迭代器,而不是反向迭代器。但无论如何,我觉得这种逻辑是相当低效的

另外,我尝试使用常规迭代器代替反向迭代器,从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--;
       }
   }
}

您不能取消对迭代器::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;

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

  • 使用
    itr1
    调用
    erase()
    后,您已使
    itr2
    无效
  • 当使用
    反向迭代器
    反向遍历序列时,您希望使用
    ++
    ,而不是
    --
    (这就是反向迭代器存在的原因)
现在,为了改进逻辑,您可以通过找到第一个不想擦除的字符并擦除所有字符来避免逐个擦除每个字符
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()
来获取与
反向迭代器相对应的“常规”迭代器。我发现是否需要调整
base()
的逻辑令人困惑(反向迭代器通常会让我困惑)——在这种情况下,这不是因为我们碰巧想在找到字符后开始擦除


Scott Meyers的这篇文章在本节中对
反向迭代器::base()
进行了很好的介绍。“准则3:了解如何使用反向迭代器的基迭代器”。那篇文章中的信息也被纳入了迈耶的《有效STL》一书中。

你肯定做错了,你正在使用
itr1
itr2
在你删除它们之后,你应该用
erase
返回的值来替换它们。忘了这一点,谢谢你指出这一点!首先,是的,我知道,我先减小它,我的编译器(VisualStudio2010)仍然给我那个错误。对不起,我应该在我的问题中说明这一点。其次,我确实处理了这个错误,只是没有把它放在我问题中的代码中。我想我之所以会犯这个错误是因为Matteo Italia上面说的,我忘记了重新分配迭代器。不,如果我使用常规迭代器,它仍然会给我itr2的“字符串迭代器不可取消引用”,将它设置为str.end()然后先把它减量。我明天再看,显然我太困了,不能正常思考。埃尔布拉特,谢谢你帮我。事实上,不是这样的。在某些地方,我没有处理错误案例,但它确实适用于某些字符串。再次感谢你!迈克尔,谢谢你,这看起来很有用,而且是更好的做事方式。我会调查的。我还发布了我在原始问题中提出的一个草率的解决方案。@rhino:注意,在
find\u if()之后,我去掉了
if
测试
:它们是不必要的,事实上会阻止只包含标点符号的字符串被修剪为空字符串。另外请注意,与其使用一个小包装函数传递给
find\u if()
您可以更像STL,使用header中的函数对象帮助程序
:而不是
not\u punt
传入
not1(ptr\u fun(ispunct))
然而,坦率地说,我发现这种东西的可读性通常比不上。