Reputation: 2540
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
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
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
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