C ++ удаление пунктуации в строках, erase () / проблема с итератором - PullRequest
3 голосов
/ 15 мая 2011

Я знаю, что я не первый, кто поднимает проблему с обратными итераторами, пытающимися вызвать метод erase () для строк.Однако я не смог найти хороших способов обойти это.

Я читаю содержимое файла, который содержит несколько слов.Когда я читаю слово, я хочу передать его функции, которую я назвал stripPunct.Тем не менее, я ТОЛЬКО хочу убрать пунктуацию в начале и конце строки, а не в середине.

Так, например:

(слово) should strip '('и') 'в результате просто слово

не! должно убрать'! 'в результате просто не

Итак, моя логика (которую, я уверен, можно улучшить) состояла в том, чтобы иметь два цикла 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--;
       }
   }
}

Ответы [ 3 ]

4 голосов
/ 15 мая 2011

Во-первых, обратите внимание на пару проблем с вашим кодом:

  • после того, как вы позвонили 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(), что сбивает с толку (обратные итераторы в общем меня смущают) - в этом случае это не так, потому что мы хотим начать стирание после найденного символа.

Эта статья Скотта Мейерса, http://drdobbs.com/cpp/184401406,, имеет хорошее отношение reverse_iterator::base() в разделе.«Указание 3: понять, как использовать базовый итератор reverse_iterator».Информация в этой статье также была включена в книгу Мейера «Эффективный STL».

0 голосов
/ 15 мая 2011

Если вы не против отрицательной логики, вы можете сделать следующее:

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;
0 голосов
/ 15 мая 2011

Вы не можете разыменовать iterator :: end (), потому что он указывает на недопустимую память (память сразу после конца массива), поэтому вы должны сначала уменьшить ее.

И последнее замечание: если слово состоит только из знаков препинания, ваша программа потерпит неудачу, обязательно обработайте это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...