2012-06-09 3 views
1

У меня есть небольшая проблема с моей реализацией string :: find.string :: find Issue (C++)

Ввод - длинная строка, состоящая из этого возможного примера: input = "one_thousand_and_fifty_one".

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

Это мой код до сих пор, который находит «и», но удаляется только тогда, когда буква перед «a» не является «s» (что указывает «тысяча»).

string toKill = "and"; 

size_t andF = input.find(toKill); 
    if (andF != string::npos) { 
     if (input[(andF - 1)] != 's') { 
      input.erase(andF, 4); 
     } 
    } 

EDIT: Я забыл упомянуть, что только другое слово на входе, который содержит «и» есть «тысяча», так что это особый случай.

+0

Так что же вы делаете с мужем, или band, или grand, или Android? Возможно, вам захочется проверить не альфа, а не просто? Возможно, вы захотите проверить и после этого слова. – paxdiablo

+4

Ну, для «android» он сработает, так как он получит доступ за пределами границ строки: 'input [-1]'. –

+0

@ Майкл, я знаю, что это будет делать, я действительно больше спрашивал, что делать плакат _wanted_ :-) – paxdiablo

ответ

3

Попробуйте это:

string toKill = "and"; 
size_t andF = 0; 

while ((andF = input.find(toKill, andF)) != string::npos) { 
    if (andF == 0 || input[andF - 1] != 's') { 
     input.erase(andF, 4); 
    } 
    else ++andF; 
} 
+0

Это работает просто и блестяще. Спасибо. – Edge

+0

Я бы удалил hardcoding из 4, просто чтобы сделать его более общим. –

+0

Похоже, я забыл о необходимости цикла while. – Edge

2

Я хотел бы использовать регулярное выражение для этого (от повышения, PCRE или стандарта C++ 11) - но если бы я должен был сделать это сам, мой код будет выглядеть вроде этого:

string toKill = "and"; 
size_t pos = 0; 
while((pos = s.find(toKill, pos))!=std::string::n_pos) 
{ 
    //Check it doesn't start with an additional letter 
    if(pos!=0 && is_alpha(s[pos-1])) { pos++; continue; } 
    //Check it doesn't end with an additional letter 
    if(pos+toKill.size()!=s.size() && is_alpha(s[pos+toKill.size()]) { pos++; continue;} 
    //Remove it and the trailing whitespace (or punctuation) 
    s.erase(pos,toKill.size()+1); 
} 
+0

Бесконечный цикл, если строка содержит тысячи. – fbafelipe

+0

Вам нужно будет искать из 'pos', если вы не назовете' erase() 'иначе вы получите бесконечный цикл – Attila

+0

Yep. Исправлено. –

2

Вам нужно (по крайней мере) две другие вещи в вашем коде. Первый - это цикл для обработки всей строки для строк and, а второй - возможность пропустить уже проверенные.

Возможно, вы также захотите обработать возможность того, что строка может начать с and, несмотря на то, что это маловероятно: будьте либеральны с тем, что вы ожидаете и конкретным с тем, что вы доставляете.

Следующий код будет хорошей отправной точкой:

#include <iostream> 
#include <string> 

int main (void) { 
    std::string inputStr = "one thousand and fifty one"; 
    std::string killStr = "and "; 

    size_t startPos = 0; 
    size_t andPos; 
    while ((andPos = inputStr.find (killStr, startPos)) != std::string::npos) { 
     if ((andPos == 0) || (inputStr[(andPos - 1)] != 's')) { 
      inputStr.erase(andPos, killStr.length()); 
      startPos = andPos; 
     } else { 
      startPos = andPos + 1; 
     } 
    } 

    std::cout << inputStr << '\n'; 
    return 0; 
} 

И, так как я был параноидальным о том, and в начале строки, и Майкл справедливо назвал меня не обрабатывает его в конец строки (а), вы можете изменить его, сделать это сделать что-то вроде:

#include <iostream> 
#include <string> 
#include <cstring> 

static bool endsWith (std::string s1, std::string s2) { 
    size_t s1Len = s1.length(); 
    size_t s2Len = s2.length(); 
    if (s2Len > s1Len) 
     return false; 
    return (strcmp (s1.c_str() + s1Len - s2Len, s2.c_str()) == 0); 
} 

int main (void) { 
    std::string inputStr = "and one thousand and fifty one thousand and"; 
    std::string killStr = "and "; 

    size_t startPos = 0; 
    size_t andPos; 
    while ((andPos = inputStr.find (killStr, startPos)) != std::string::npos) { 
     if ((andPos == 0) || (inputStr[(andPos - 1)] != 's')) { 
      inputStr.erase (andPos, killStr.length()); 
      startPos = andPos; 
     } else { 
      startPos = andPos + 1; 
     } 
    } 
    if (!endsWith (inputStr, "sand") && endsWith (inputStr, "and")) 
     inputStr.erase (inputStr.length() - 3); 

    std::cout << inputStr << '\n'; 
    return 0; 
} 

(а) Если я собираюсь быть педантом, я бы лучше хорошо делать это правильно :-)

+0

Мне нравится добавление пространства в строку toKill - это исправление проблемы с завершающим символом. Однако он не поймает конечную «и» - как «» сто и «», но я предполагаю, что это нормально для случая OPs. –