Edge
Edge

Reputation: 2540

string::find Issue (C++)

I've got a little issue with my string::find implementation.

The input is a long string, which consist of this possible example: input = "one_thousand_and_fifty_one".

My issue seems to be, that in an input string that contains more than one 'and', only the first and is removed, and the others aren't.

This is my code so far, which finds "and", but only removes is when the letter before 'a' isn't 's' (which indicates "thousand").

string toKill = "and";

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

EDIT: I forgot to mention, that the only other word in the input that contains 'and' is 'thousand', so this IS a special case.

Upvotes: 1

Views: 1202

Answers (3)

paxdiablo
paxdiablo

Reputation: 881113

You need (at least) two other things in your code. The first is a loop to process the entire string for and strings and the second is the ability to skip ones already checked.

You may also want to handle the possibility that the string may start with and, despite that being unlikely: be liberal with what you expect and specific with what you deliver.

The following code would be a good starting point:

#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, since I was being paranoid about having and at the start of the string, and Michael rightly called me on not handling it at the end of the string (a), you can modify it do do so with something like:

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

(a) If I'm going to be a pedant, I'd better well do it properly :-)

Upvotes: 2

fbafelipe
fbafelipe

Reputation: 4952

Try this:

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

Upvotes: 3

Michael Anderson
Michael Anderson

Reputation: 73450

I'd use a regex for this (from boost, PCRE or the C++11 standard) - but if I had to do it myself, my code would look kind of like this:

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

Upvotes: 2

Related Questions