C++ Input to only accept operator symbols or only numbers - c++

I've been trying to code this as sort of a beginner program for C++ to help me understand the language more but I don't really know how to tackle this.
Basically the program needs to only accept '+', '*', '/', '%' or numbers, otherwise it will say the input is invalid.
Here's what I've done so far
#include <iostream>
#include <string>
using namespace std;
int main()
{
string x;
cout<<"Enter expression: ";
getline(cin,x);
if(x.find('*') != string::npos){
cout<<"Multiplication";
}else if(x.find('/') != string::npos){
cout<<"Division";
}else if(x.find('%') != string::npos){
cout<<"Modulo";
}else if(x.find('+') != string::npos){
cout<<"Addition";
}else{
cout<<"Invalid!";
}
return 0;
}

Definition of the Valid Input
Here I assume that the valid input is given by the following natural statements. First of all, as you mentioned,
Each input must be constructed from *, /, %, + and an integer.
Only zero or one operation. ( So 1+1 is valid. )
For an input of an integer, I also assume
Whitespace characters are allowed in the left and right side of the input string.
Whitespace characters between non-whitespace characters are not allowed.
The first non-whitespace character must be 0, 1, ..., 9 or - (for negative integers).
The second and the subsequent non-whitespace characters must be 0, 1, ..., 8 or 9.
Note that in my assumption the positive sign character +, decimal-point character . are not allowed for integer inputs.
For instance, in this definition,
"123", " 123", "123 " and " -123 " are all valid integer inputs.
"abc", "123a", " 1 23", "+123" and "1.0" are all invalid ones.
Validity Check Function for An Integer
First, to check the validity of the input of an integer, we trim the input and remove left and right whitespaces using the following trimming function: ( If you can use C++17, std::string_view would be more preferable from the performance poin of view.)
#include <string>
std::string trimLR(const std::string& str)
{
const auto strBegin = str.find_first_not_of(" \f\n\r\t\v");
if (strBegin == std::string::npos){
return "";
}
const auto strEnd = str.find_last_not_of(" \f\n\r\t\v");
const auto strRange = strEnd - strBegin + 1;
return str.substr(strBegin, strRange);
}
Next, we define the following simple validity check function isInteger which checks whether the passed string is an integer or not. Here std::isdigit is useful to check whether each character is digits or not.
Please note that various interesting methods are proposed in the past
posts.
#include <string>
#include <algorithm>
bool isInteger(const std::string& s)
{
const auto ts = trimLR(s);
if(ts.empty()){
return false;
}
const std::size_t offset = (ts[0] == '-') ? 1 : 0;
const auto begin = ts.cbegin() + offset;
return (begin != ts.cend()) // false if s is just a negative sign "-"
&& std::all_of(begin, ts.cend(), [](unsigned char c){
return std::isdigit(c);
});
}
Main Function
Now it is easy and straightforward to implement the main function.
The following code will check inputs and work fine.
The next considerations are writing tests and performance tunings:
DEMO(Multiplication)
DEMO(Division)
DEMO(Modulo)
DEMO(Addition)
DEMO(Invalid 1)
DEMO(Invalid 2)
#include <iostream>
int main()
{
std::string x;
std::cout << "Enter expression: ";
std::getline(std::cin, x);
const auto optPos = x.find_first_of("*/%+");
if (optPos == std::string::npos)
{
if(isInteger(x)){
std::cout << "Valid input, " << x;
}
else{
std::cout << "Invalid input, " << x;
}
return 0;
}
const auto left = x.substr(0, optPos);
const auto opt = x.substr(optPos, 1);
const auto right = x.substr(std::min(optPos+1, x.length()-1));
if (!isInteger(left) || !isInteger(right))
{
std::cout
<< "Either `" << left << "`, `" << right
<< "` or both are invalid inputs." << std::endl;
return 0;
}
const auto leftVal = std::stod(left);
const auto rightVal = std::stod(right);
if(opt == "*")
{
std::cout
<< "Multiplication: "
<< x << " = " << (leftVal * rightVal);
}
else if(opt == "/")
{
std::cout
<< "Division: "
<< x << " = " << (leftVal / rightVal);
}
else if(opt == "%")
{
std::cout
<< "Modulo: "
<< x << " = " << (std::stoi(left) % std::stoi(right));
}
else if(opt == "+")
{
std::cout
<< "Addition: "
<< x << " = " << (leftVal + rightVal);
}
return 0;
}

Related

How can i split adjacent numbers and letters in c++?

I've got a large text document that including adjacent numbers and letters.
Just like that,
JACK1940383DAVID30284HAROLD68372TROY4392 etc.
How can i split this like below in C++
List: Jack / 1940383 , David/30284, ...
You can use std::string::find_first_of() and std::string::find_first_not_of() in a loop, using std::string::substr() to extract each piece, eg:
std::string s = "JACK1940383DAVID30284HAROLD68372TROY4392";
std::string::size_type start = 0, end;
while ((end = s.find_first_of("0123456789", start)) != std::string::npos) {
std::string name = s.substr(start, end-start);
start = end;
int number;
if ((end = s.find_first_not_of("0123456789", start)) != std::string::npos) {
number = std::stoi(s.substr(start, end-start));
}
else {
number = std::stoi(s.substr(start));
}
start = end;
// use name and number as needed...
}
Online Demo
You can use regex like this:
#include <iostream>
#include <string>
#include <regex>
#include <vector>
// create a struct to group your data
// this makes it easy to store it in a vector.
struct person_t
{
std::string name;
std::string number;
};
// overloaded output operator for printing one person's details
std::ostream& operator<<(std::ostream& os, const person_t& person)
{
std::cout << person.name << ": " << person.number << std::endl;
return os;
}
// get a vector of person_t based on the input
auto get_persons(const std::string& input)
{
// make a regex in this case a regex that will match one or more capital letters
// and groups them using the ()
// then match one or more digits and group them too.
static const std::regex rx{ "([A-Z]+)([0-9]+)" };
std::smatch match;
// a vector to hold all the persons
std::vector<person_t> persons;
// start at begin of string and look for first part of the string
// that matches the regex.
auto cbegin = input.cbegin();
while (std::regex_search(cbegin, input.cend(), match, rx))
{
// match[0] will contain the whole match,
// match[1]-match[n] will contain the groups from the regular expressions
// match[1] will contain the match with characters and thus the name
// match[2] will contain the match with the numbers and thus the number.
// create a person_t struct with this info
person_t person{ match[1], match[2] };
// and add it to the vector
persons.push_back(person);
cbegin = match.suffix().first;
}
return persons;
}
int main()
{
// parse and split the string
auto persons = get_persons("JACK1940383DAVID30284HAROLD68372TROY4392");
// show the output
for (const auto& person : persons)
{
std::cout << person;
}
}
As pointed in other good answers you can use
find_first_of(), find_first_not_of() and substr() from std::string in a loop
regex
But it may be too much. I will add 3 more examples that you may find
simpler.
The first 2 programs expects the file name on the command line for (my) convenience here, and the test file is in.txt. Contents are the same as posted
JACK1940383DAVID30284HAROLD68372TROY4392
The last example just parses the string data declared as a char[]
1. Using fscanf()
Since the target is to consume formatted data, fscanf() is an option. As the data structure is very simple, the program is just a one line loop:
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
program output
output is the same for all examples
JACK/1940383
DAVID/30284
HAROLD/68372
TROY/4392
code for ex. 1
#include <errno.h>
#include <iostream>
int main(int argc,char** argv)
{
if (argc < 2)
{ std::cerr << "Use: pgm FileName\n";
return -1;
}
FILE* F = fopen(argv[1], "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
std::cerr << "File: \"" << argv[1] << "\"\n";
char tk_key[50], tk_value[50];
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
fclose(F);
return 0;
}
using a state machine
There are just 2 states so it is not a fancy FSA ;) State machines are good for representing this kind of stuff, albeit here this seems to be overkill.
#define S_LETTER 0
#define S_DIGIT 1
#include <algorithm>
#include <iostream>
#include <fstream>
using iich = std::istream_iterator<char>;
int main(int argc,char** argv)
{
std::ifstream in_file{argv[1]};
if ( not in_file.good()) return -1;
iich p {in_file}, eofile{};
std::string token{}; // string to build values
char st = S_LETTER; // state value for FSA
std::for_each(p, eofile,
[&token,&st](char ch)
{
char temp = 0;
switch (st)
{
case S_LETTER:
if ((ch >= '0') && (ch <= '9'))
{
std::cout << token << "/";
token = ch;
st = S_DIGIT; // now in number
}
else token += ch; // concat in string
break;
case S_DIGIT:
default:
if ((ch < '0') || (ch > '9'))
{ // is a letter
std::cout << token << "\n";
token = ch;
st = S_LETTER; // now in name
}
else token += ch; // concat in string
break;
}; // switch()
});
std::cout << token << "\n"; // print last token
}
Here we have no loop. for_each gets the data from an iterator and passes it to a function that builds the name and the value as strings and couts them
Output is the same
3. a simple FSA to consume the data
#define S_LETTER 0
#define S_DIGIT 1
#include <iostream>
int main(void)
{
char one[] = "JACK1940383DAVID30284HAROLD68372TROY4392";
char* p = (char*)&one;
char* token = p;
char st = S_LETTER;
char temp = 0;
while (*p != 0)
{
switch (st)
{
case S_LETTER:
if ((*p >= '0') && (*p <= '9'))
{
temp = *p;
*p = 0;
std::cout << token << "/";
*p = temp;
token = p;
st = S_DIGIT; // now in number
}
break;
case S_DIGIT:
default:
if ( (*p < '0') || (*p > '9'))
{ // letter
temp = *p;
*p = 0;
std::cout << token << "\n";
*p = temp;
token = p;
st = S_LETTER; // now in name
}
break;
}; // switch()
p += 1; // next symbol
}; // while()
std::cout << token << "\n"; // print last token
}
This code just uses a C-style loop to parse the input data

how to use + as a character in c++ string

I am creating a bool function to test the validity of an input string. One of the specs requires testing the placement of a + symbol. However, when I try and search for '+' within the string, nothing results. I am thinking this is because + is an operator? I have also tried using '+' and creating a substring at this location with no success.
simplified version of code:
bool isValidString(string s)
{
size_t found1 = s.find_first_not of("123456789") //string should only contain numbers, B, and +
if ( s[found1] == 'B' ) {
found1++;
if s[found1] == '+'
return true;
else
return false; }
}
If I may guess at your intent ...
Here are 3 ways to detect 'B+', in your strings, but the 3rd does not meet your requirements.
#include <iostream>
#include <iomanip>
#include <vector>
// returns true when "B+" found in s
bool isValidString1 (std::string s)
{
bool retVal = false;
size_t found1 = s.find_first_not_of("123456789"); //string should only contain numbers, B, and +
if ( s[found1] == 'B' )
{
found1++;
retVal = (s[found1] == '+');
}
return retVal;
}
bool isValidString2 (std::string s)
{
size_t found1 = s.find_first_not_of("123456789"); //string should only contain numbers, B, and +
bool retVal = false;
switch (s[found1])
{
case 'B': retVal = ('+' == s[found1+1]); break;
case '+': /* tbd - what do if out of order */ break;
default : /* tbd - what do if not allowed */ break;
}
return (retVal);
}
// simple, but does not reject the rest of non-digits
bool isValidString3 (std::string s)
{
size_t indx = s.find("B+");
return (indx != std::string::npos);
}
void test(std::string s)
{
std::cout << "\n 1 s: '" << s << "' "
<< (isValidString1(s) ? "valid" : "invalid");
std::cout << "\n 2 s: '" << s << "' "
<< (isValidString2(s) ? "valid" : "invalid");
std::cout << "\n 3 s: '" << s << "' "
<< (isValidString3(s) ? "valid" : "invalid") << std::endl;
}
int main(int , char** )
{
std::string s10 = "1234B+56789";
test(s10);
std::string s11 = "1234+B+5678";
test(s11);
std::string s12 = "B+12345678";
test(s12);
std::string s13 = "12345678B+";
test(s13);
std::string s14 = "12345678+B";
test(s14);
}
Output looks like:
1 s: '1234B+56789' valid
2 s: '1234B+56789' valid
3 s: '1234B+56789' valid
1 s: '1234+B+5678' invalid
2 s: '1234+B+5678' invalid
3 s: '1234+B+5678' valid
1 s: 'B+12345678' valid
2 s: 'B+12345678' valid
3 s: 'B+12345678' valid
1 s: '12345678B+' valid
2 s: '12345678B+' valid
3 s: '12345678B+' valid
1 s: '12345678+B' invalid
2 s: '12345678+B' invalid
3 s: '12345678+B' invalid
It appears that the following (output from isValidStr3()) is actually not what you want.
3 s: '1234+B+5678' valid
I find no issue with '+', it is simply another char in a std::string. No escaping needed. In this context, it is not an operator.
this seems to work
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str{ "Smith, where Jones + + \"+ +\", \"+ +\" +."
" \"+ +\" + + the examiners' approval." };
string substr{ '+' };
cout << "The string to be searched is:" << endl << str << endl;
size_t offset{};
size_t count{};
size_t increment{ substr.length() };
while (true)
{
offset = str.find(substr, offset);
if (string::npos == offset)
break;
offset += increment;
++count;
}
cout << " The string \"" << substr
<< "\" was found " << count << " times in the string above."
<< endl;
return 0;
}

How to return a certain boolean value in a recursive function?

I want to make a recursive function that determines if a string's characters all consist of alphabets or not. I just can't figure it out. Here's what I've done so far but it doesn't work properly.
bool isAlphabetic(string s){
const char *c = s.c_str();
if ((!isalpha(c[0]))||(!isalpha(c[s.size()])))
{
return false;
}
else if (isalpha(c[0]))
{
isAlphabetic(c+1);
return true;
}
}
can anyone suggest a correct way?
Leaving aside the many partial strings you'll create (consider passing in just the string and a starting index instead), the isalpha(c[s.size()]) check will always fail, since that's the \0 at the end of the string. You're also ignoring the result of the recursive calls.
bool isAlphabetic(string s){
if (s.size() < 1)
return true; // empty string contains no non-alphas
const char *c = s.c_str();
if (!isalpha(c[0]))
{
return false; // found a non-alpha, we're done.
}
else
{
return isAlphabetic(c+1); // good so far, try the rest of the string
}
}
Building on Paul's answer, here is a fixed implementation that won't copy any portion of the string. It accomplishes this by passing a reference to the string object and an index to the character to check; recursion simply adds 1 to this index to check the next character, and so on until the end of the string is found.
I have removed your call to c_str() since it isn't needed. string can be directly indexed.
bool isAlphabetic(string const & s, int startIndex = 0) {
// Terminating case: End of string reached. This means success.
if (startIndex == s.size()) {
return true;
}
// Failure case: Found a non-alphabetic character.
if (!isalpha(s[startIndex])) {
return false;
}
// Recursive case: This character is alphabetic, so check the rest of the string.
return isAlphabetic(s, startIndex + 1);
}
Note that the empty string is considered alphabetic by this function. You can change this by changing return true to return !s.empty().
Here a working example:
#include <iostream>
#include <string>
using namespace std;
bool isAlphabetic(string s)
{
if( s.empty() )
{
return false;
}
cout << "checking: " << s[0] << endl;
if( isalpha(s[0]) )
{
return true;
}
return isAlphabetic(&s[0]+1);
}
int main()
{
string word0 = "test";
if( isAlphabetic(word0) )
{
cout << word0 << " is alphabetic" << endl;
}
else
{
cout << word0 << " is NOT alphabetic" << endl;
}
string word1 = "1234";
if( isAlphabetic(word1) )
{
cout << word1 << " is alphabetic" << endl;
}
else
{
cout << word1 << " is NOT alphabetic" << endl;
}
string word2 = "1234w";
if( isAlphabetic(word2) )
{
cout << word2 << " is alphabetic" << endl;
}
else
{
cout << word2 << " is NOT alphabetic" << endl;
}
return 0;
}

Return fixed length std::string from integer value

Problem -> To return fixed length string to std::string*.
Target machine -> Fedora 11 .
I have to derive a function which accepts integer value and return fixed lenght string to a string pointer;
for example -> int value are in range of 0 to -127
so for int value 0 -> it shoud display 000
for value -9 -> it should return -009
for value say -50 -> it should return -050
for value say -110 -> it should return -110
so in short , lenght should be same in all cases.
What I have done : I have defined the function according to the requirement which has shown below.
Where I need help: I have derived a function but I am not sure if this is correct approach. When I test it on standalone system on windows side , the exe stopped working after sometimes but when I include this function with the overall project on Linux machine , it works flawlessly.
/* function(s)to implement fixed Length Rssi */
std::string convertString( const int numberRssi, std::string addedPrecison="" )
{
const std::string delimiter = "-";
stringstream ss;
ss << numberRssi ;
std::string tempString = ss.str();
std::string::size_type found = tempString.find( delimiter );
if( found == std::string::npos )// not found
{
tempString = "000";
}
else
{
tempString = tempString.substr( found+1 );
tempString = "-" +addedPrecison+tempString ;
}
return tempString;
}
std::string stringFixedLenght( const int number )
{
std::string str;
if( (number <= 0) && (number >= -9) )
{
str = convertString( number, "00");
}
else if( (number <= -10) && (number >= -99) )
{
str = convertString( number, "0");
}
else
{
str= convertString(number, "");
}
return str;
}
// somewhere in the project calling the function
ErrorCode A::GetNowString( std::string macAddress, std::string *pString )
{
ErrorCode result = ok;
int lvalue;
//some more code like iopening file and reading file
//..bla
// ..bla
// already got the value in lvalue ;
if( result == ok )
{
*pString = stringFixedLenght( lValue );
}
// some more code
return result;
}
You can use I/O manipulators to set the width that you need, and fill with zeros. For example, this program prints 00123:
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
cout << setfill('0') << setw(5) << 123 << endl;
return 0;
}
You have to take care of the negative values yourself, though: cout << setfill('0') << setw(5) << -123 << endl prints 0-123, not -0123. Check if the value is negative, set the width to N-1, and add a minus in front.
How about using std::ostringstream and the standard output formatting manipulators?
std::string makeFixedLength(const int i, const int length)
{
std::ostringstream ostr;
if (i < 0)
ostr << '-';
ostr << std::setfill('0') << std::setw(length) << (i < 0 ? -i : i);
return ostr.str();
}
Note that your examples contradict your description: if the value is -9,
and the fixed length is 3, should the output be "-009" (as in your
example), or "-09" (as you describe)? If the former, the obvious
solution is to just use the formatting flags on std::ostringstream:
std::string
fixedWidth( int value, int width )
{
std::ostringstream results;
results.fill( '0' );
results.setf( std::ios_base::internal, std::ios_base::adjustfield );
results << std::setw( value < 0 ? width + 1 : width ) << value;
return results.str();
}
For the latter, just drop the conditional in the std::setw, and pass
width.
For the record, although I would avoid it, this is one of the rare cases
where printf does something better than ostream. Using snprintf:
std::string
fixedWidth( int value, int width )
{
char buffer[100];
snprintf( buffer, sizeof(buffer), "%.*d", width, value );
return buffer;
}
You'd probably want to capture the return value of snprintf and add
some error handling after it, just in case (but 100 chars is
sufficient for most current machines).
I have nothing against the versions that use streams, but you can do it all yourself more simply than your code:
std::string fixedLength(int value, int digits = 3) {
unsigned int uvalue = value;
if (value < 0) {
uvalue = -uvalue;
}
std::string result;
while (digits-- > 0) {
result += ('0' + uvalue % 10);
uvalue /= 10;
}
if (value < 0) {
result += '-';
}
std::reverse(result.begin(), result.end());
return result;
}
like this?
#include <cstdlib>
#include <string>
template <typename T>
std::string meh (T x)
{
const char* sign = x < 0 ? "-" : "";
const auto mag = std::abs (x);
if (mag < 10) return sign + std::string ("00" + std::to_string(mag));
if (mag < 100) return sign + std::string ("0" + std::to_string(mag));
return std::to_string(x);
}
#include <iostream>
int main () {
std::cout << meh(4) << ' '
<< meh(40) << ' '
<< meh(400) << ' '
<< meh(4000) << '\n';
std::cout << meh(-4) << ' '
<< meh(-40) << ' '
<< meh(-400) << ' '
<< meh(-4000) << '\n';
}
004 040 400 4000
-004 -040 -400 -4000

C++ string diff (a la Python's difflib)

I'm trying to diff two strings to determine whether or not they solely vary in one numerical subset of the string structure; for example,
varies_in_single_number_field('foo7bar', 'foo123bar')
# Returns True, because 7 != 123, and there's only one varying
# number region between the two strings.
In Python I can use the difflib to accomplish this:
import difflib, doctest
def varies_in_single_number_field(str1, str2):
"""
A typical use case is as follows:
>>> varies_in_single_number_field('foo7bar00', 'foo123bar00')
True
Numerical variation in two dimensions is no good:
>>> varies_in_single_number_field('foo7bar00', 'foo123bar01')
False
Varying in a nonexistent field is okay:
>>> varies_in_single_number_field('foobar00', 'foo123bar00')
True
Identical strings don't *vary* in any number field:
>>> varies_in_single_number_field('foobar00', 'foobar00')
False
"""
in_differing_substring = False
passed_differing_substring = False # There should be only one.
differ = difflib.Differ()
for letter_diff in differ.compare(str1, str2):
letter = letter_diff[2:]
if letter_diff.startswith(('-', '+')):
if passed_differing_substring: # Already saw a varying field.
return False
in_differing_substring = True
if not letter.isdigit(): return False # Non-digit diff character.
elif in_differing_substring: # Diff character not found - end of diff.
in_differing_substring = False
passed_differing_substring = True
return passed_differing_substring # No variation if no diff was passed.
if __name__ == '__main__': doctest.testmod()
But I have no idea how to find something like difflib for C++. Alternative approaches welcome. :)
This might work, it at least passes your demonstration test:
EDIT: I've made some modifications to deal with some string indexing issues. I believe it should be good now.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
bool starts_with(const std::string &s1, const std::string &s2) {
return (s1.length() <= s2.length()) && (s2.substr(0, s1.length()) == s1);
}
bool ends_with(const std::string &s1, const std::string &s2) {
return (s1.length() <= s2.length()) && (s2.substr(s2.length() - s1.length()) == s1);
}
bool is_numeric(const std::string &s) {
for(std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
if(!std::isdigit(*it)) {
return false;
}
}
return true;
}
bool varies_in_single_number_field(std::string s1, std::string s2) {
size_t index1 = 0;
size_t index2 = s1.length() - 1;
if(s1 == s2) {
return false;
}
if((s1.empty() && is_numeric(s2)) || (s2.empty() && is_numeric(s1))) {
return true;
}
if(s1.length() < s2.length()) {
s1.swap(s2);
}
while(index1 < s1.length() && starts_with(s1.substr(0, index1), s2)) { index1++; }
while(ends_with(s1.substr(index2), s2)) { index2--; }
return is_numeric(s1.substr(index1 - 1, (index2 + 1) - (index1 - 1)));
}
int main() {
std::cout << std::boolalpha << varies_in_single_number_field("foo7bar00", "foo123bar00") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("foo7bar00", "foo123bar01") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("foobar00", "foo123bar00") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("foobar00", "foobar00") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("7aaa", "aaa") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("aaa7", "aaa") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("aaa", "7aaa") << std::endl;
std::cout << std::boolalpha << varies_in_single_number_field("aaa", "aaa7") << std::endl;
}
Basically, it looks for a string which has 3 parts, string2 begins with part1, string2 ends with part3 and part2 is only digits.
It's probably a bit of overkill, but you could use boost to interface to python. At the worst, difflib is implemented in pure python, and it's not too long. It should be possible to port from python to C...
You could do an ad hoc approach: You're looking to match strings s and s', where s=abc and s'=ab'c, and the b and b' should be two distinct numbers (possible empty). So:
Compare the strings from the left, char by char, until you hit different characters, and then stop. You
Similarly, compare the strings from the right until you hit different characters, OR hit that left marker.
Then check the remainders in the middle to see if they're both numbers.
How about using something like boost::regex?
// pseudo code, may or may not compile
bool match_except_numbers(const std::string& s1, const std::string& s2)
{
static const boost::regex fooNumberBar("foo\\d+bar");
return boost::match(s1, fooNumberBar) && boost::match(s2, fooNumberBar);
}
#Evan Teran: looks like we did this in parallel -- I have a markedly less readable O(n) implementation:
#include <cassert>
#include <cctype>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
ostringstream debug;
const bool DEBUG = true;
bool varies_in_single_number_field(const string &str1, const string &str2) {
bool in_difference = false;
bool passed_difference = false;
string str1_digits, str2_digits;
size_t str1_iter = 0, str2_iter = 0;
while (str1_iter < str1.size() && str2_iter < str2.size()) {
const char &str1_char = str1.at(str1_iter);
const char &str2_char = str2.at(str2_iter);
debug << "str1: " << str1_char << "; str2: " << str2_char << endl;
if (str1_char == str2_char) {
if (in_difference) {
in_difference = false;
passed_difference = true;
}
++str1_iter, ++str2_iter;
continue;
}
in_difference = true;
if (passed_difference) { /* Already passed a difference. */
debug << "Already passed a difference." << endl;
return false;
}
bool str1_char_is_digit = isdigit(str1_char);
bool str2_char_is_digit = isdigit(str2_char);
if (str1_char_is_digit && !str2_char_is_digit) {
++str1_iter;
str1_digits.push_back(str1_char);
} else if (!str1_char_is_digit && str2_char_is_digit) {
++str2_iter;
str2_digits.push_back(str2_char);
} else if (str1_char_is_digit && str2_char_is_digit) {
++str1_iter, ++str2_iter;
str1_digits.push_back(str1_char);
str2_digits.push_back(str2_char);
} else { /* Both are non-digits and they're different. */
return false;
}
}
if (in_difference) {
in_difference = false;
passed_difference = true;
}
string str1_remainder = str1.substr(str1_iter);
string str2_remainder = str2.substr(str2_iter);
debug << "Got to exit point; passed difference: " << passed_difference
<< "; str1 digits: " << str1_digits
<< "; str2 digits: " << str2_digits
<< "; str1 remainder: " << str1_remainder
<< "; str2 remainder: " << str2_remainder
<< endl;
return passed_difference
&& (str1_digits != str2_digits)
&& (str1_remainder == str2_remainder);
}
int main() {
assert(varies_in_single_number_field("foo7bar00", "foo123bar00") == true);
assert(varies_in_single_number_field("foo7bar00", "foo123bar01") == false);
assert(varies_in_single_number_field("foobar00", "foo123bar00") == true);
assert(varies_in_single_number_field("foobar00", "foobar00") == false);
assert(varies_in_single_number_field("foobar00", "foobaz00") == false);
assert(varies_in_single_number_field("foo00bar", "foo01barz") == false);
assert(varies_in_single_number_field("foo01barz", "foo00bar") == false);
if (DEBUG) {
cout << debug.str();
}
return 0;
}