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