I am trying to verify if a specific string is in the input string and if so do something, based on the found string; but it seems that it is always doing the first task no matter what...
if (inputString.find(str1) >= 0)
{
//do something
}
else if (inputString.find(str2) >= 0)
{
// do something else
}
else
{
std::cout << "Strange" << std::endl;
}
It is always entering the // do something block no matter if the str1 is present in inputString or not.
If I do
int str1pos = inputString.find(str1);
int str2pos = inputString.find(str2);
if (str1pos >= 0)
{
//do something
}
else if (str2pos >= 0)
{
// do something else
}
else
{
std::cout << "Strange" << std::endl;
}
it seems to work. Why is that? What am I doing wrong?
inputString.find(str1) >= 0 is always true.
This is because the return type of find is size_t which is an unsigned integer type, so it cannot be negative. Any decent compiler will give a warning for this comparison.
In your second example, when you convert the return value of find to int, if find returned npos, then the value becomes -1. That's why >= 0 works there. But if find returned a value greater than INT_MAX but not npos, the cast would turn the index to a negative value, and your logic would fail.
Therefore, you should compare to npos instead:
if (inputString.find(str1) != std::string::npos)
std::string::find returns std::string::npos if the input string is not found. To check if the string contains your input string, you must use:
if (inputString.find(str1) != std::string::npos)
{
//do something
}
else if (inputString.find(str2) != std::string::npos)
{
// do something else
}
else
{
std::cout << "Strange" << std::endl;
}
When not found, the return value is std::string::npos. It could be a positive number. You don't know.
Change your code into
if (inputString.find(str1) != std::string::npos)
{
//do something
}
else if (inputString.find(str2) != std::string::npos)
{
// do something else
}
else
{
std::cout << "Strange" << std::endl;
}
Related
I want to implement a simple is_number function that checks if it's an integer, float or an unsigned long int using this method:
bool isNumber(const std::string& str)
{
size_t idx = 0;
//Check if it's an integer
std::stoi(str,&idx);
if (idx == str.size())
return true;
//Check if it's a float
std::stof(str,&idx);
if (idx == str.size() || str[str.size()-1] == 'f' && idx == str.size()) //Cause I do have some float numbers ending with 'f' in the database
return true;
//Check if it's an unsigned long int
std::stoul(str,&idx);
if (idx == str.size())
return true;
return false;
}
But if I test it with a pure string like "test" or "nan", it will throw an error because I'm trying to change a pure string to an integer.
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
However if I test it with "0nan" for example, stoi or the others will retrieve the first number and assign the index position of the first found number to the idx variable.
Is it possible to find a workaround for pure strings like "nan" or any other?
Or is there a better method to implement this without regex or try-catch?
std::stoi throws when it fails. Instead of using C i/o you can use C++ streams, try to read from the stream and check if there is something left in the stream:
#include <string>
#include <sstream>
#include <iostream>
enum Number {Float,Signed,Unsigned,NotANumber};
template <typename T>
bool is_only_a(const std::string& str){
std::stringstream ss(str);
T x;
return (ss >> x && ss.rdbuf()->in_avail() ==0);
}
Number isNumber(const std::string& str)
{
size_t idx = 0;
if (is_only_a<unsigned long>(str)) return Unsigned;
else if (is_only_a<int>(str)) return Signed;
else if (is_only_a<float>(str)) return Float;
return NotANumber;
}
int main() {
std::cout << isNumber("1.2") << "\n";
std::cout << isNumber("12") << "\n";
std::cout << isNumber("-12") << "\n";
std::cout << isNumber("asd") << "\n";
std::cout << isNumber("nan") << "\n";
}
Order is important, because 12 could be a float as well.
The link I posted in the comments is most probably what you need.
The only slight modification needed from the answers there is adding a +/- sign, and an optional (at most one) decimal point:
bool isNumber(const std::string &s) {
bool first_char = true;
bool saw_decpt = false;
for (const auto &it: s) {
if (std::isdigit(it)) { first_char = false; }
else if (it == '+' && first_char) { first_char = false; }
else if (it == '-' && first_char) { first_char = false; }
else if (it == '.' && !saw_decpt) { first_char = false; saw_decpt = true; }
else return false;
}
return true;
}
Here is the code:
void Reader::read(short& in) {
char* str = new char[6];
char* strbeg = str;
cin.getline(str, 6);
in = 0;
int value = 0;
short sign = 1;
if (*str == '+' || *str == '-') {
if (*str == '-' ) sign = -1;
str++;
}
while (isdigit(*str)) {
value *= 10;
value += (int) (*str - '0');
str++;
if (value > 32767) {
cout.write("Error, value can't fit short. Try again.\n", 41);
delete[] strbeg;
read(in);
return;
}
}
if (sign == -1) { value *= -1; }
in = (short) value;
delete[] strbeg;
return;
}
What happens is that if I type 999999999, it calls itself but on fourth line it's not gonna ask for input again. Debugger couldn't give much info as it is more language-specific question. Thank you in advance. Have a nice day!
Yes, the goal is to parse input as short. I know about losing 1 from min negative, wip :)
=== edit ===
I've tried goto... No, same thing. So it's not about visible variables or addresses, I guess.
=== edit ===
I can't use operator >> as it is forbidden by the task.
999999999 will cause an overflow, thus failbit is set for cin. Then your program reach read(in), then the cin.getline(). Here, beacause of failbit, cin will not ask any input again.
If you tried to figure out why in my code cin do ask for more input, you might find out all this by yourself.
I write you an example.
#include <iostream>
#include <climits>
using namespace std;
int main() {
char str[6];
short x = 0;
bool flag = false;
while (flag == false) {
cin.getline(str, 6);
flag = cin.good();
if (flag) { // if read successfully
char *p = str;
if (*p=='-') // special case for the first character
++p;
while (*p && *p>='0' && *p<='9')
++p;
if (*p) // there is a non digit non '\0' character
flag = false;
}
if (flag == false) {
cout << "An error occurred, try try again." << endl;
if (!cin.eof()) {
cin.unget(); // put back the possibly read '\n'
cin.ignore(INT_MAX, '\n');
}
cin.clear();
} else {
// str is now ready for parsing
// TODO: do your parsing work here
// for exemple x = atoi(str);
}
}
std::cout << x << std::endl;
return 0;
}
As we have discussed, you don't need new.
Check whether the string read is clean before parsing. If you mix checking and parsing, things will be complicated.
And you don't need recursion.
Read characters from stream by istream::getline seems to be the only option we have here. And when an error occurred, this function really doesn't tell us much, we have to deal with overflow and other problem separately.
Below shows the main program with an if statement. If the if statement is true, then it moves onto triggering the function.
If what in the function is true, then x = true, which triggers the final action. The, x from the aux function still comes out as undeclared.
What am I doing wrong?
void aux(bool x) {
std::string text;
std::getline(std::cin, text);
if (text.find("be ") != std::string::npos ||
text.find("do ") != std::string::npos ||
text.find("can ") != std::string::npos ||
text.find("but ") != std::string::npos) {
x = true;
}
}
int main() {
std::string text;
std::getline(std::cin, text);
if (text.find("7 ") != std::string::npos) {
aux();
{
if (x);
{
std::cout<<"A function to equal a group of words"<<std::endl;
}
}
}
getchar();
return 0;
}
You may want to change your aux function to return a boolean instead of taking it as a parameter:
bool aux() {
if (/* your checks here */) {
return true;
} else {
return false;
}
}
The bool to the left of the function name, in place of void indicates that the result of calling your function will be a boolean.
Then in main you would use aux like this:
if (aux()) {
std::cout<<"A function to equal a group of words" <<std::endl;
}
Seems like you don't know about local and global variable.
You can use pass by reference to solve the problem as follows:
void aux(bool &x)
{
std::string text;
std::getline(std::cin, text);
if(text.find("be ") != std::string::npos || text.find("do ") != std::string::npos ||
text.find("can ") != std::string::npos || text.find("but ") != std::string::npos)
{
x = true;
}
else{
x = false;
}
}
int main()
{
bool x = false;
if (text.find("7 ") != std::string::npos)
{
aux(x);
//{ un-necessary brackets.
if (x);
{
std::cout<<"A function to equal a group of words" <<std::endl;
}un-necessary brackets.
//}
}
getchar();
return 0;
}
Problem you have:
You have following mistakes in your code.
1.You have defined a function like this:
void aux(bool x){
//your code here
}
But you haven't called it by passing any variable as parameter/argument. Do as following:
aux(var1);//var1 is boolean variable
2 . The change in x doesn't appear there because the x will be treated as a new variable inside the aux(var1) function. So if you use pass by reference, then the changes will be persistence to the variable (here x).
You seem to be operating on the assumption that 'x' should be available to your main() function after calling aux(). C/C++ does not work that way - there is no 'x' in the scope of your main function because you never declared anything named x.
Furthermore, it will not have the value you want unless you return x from your aux() function and assign it to something in your main function.
You should familiarize yourself with the topic of 'scope' in C/C++ as that will prevent further misunderstanding, but the basic problem you need to solve with your sample is:
You arent passing a bool to your aux() function, so it wont work (it is a parameter for aux()
You arent returning the value of x from your aux function
You arent declaring a boolean in your main function to take the value returned from aux() function
It is completely vague what is your expectations about bool x? If you want to change the x and x is outter variable then you need either pass it by the reference or return the value. Return of the value is much more logical.
bool
aux()
{
std::string text;
std::getline(std::cin, text);
if(text.find("be ") != std::string::npos ||
text.find("do ") != std::string::npos ||
text.find("can ") != std::string::npos ||
text.find("but ") != std::string::npos)
return true;
return false
}
Now it is also logical to use the result of aux() without dangling x with random value:
int main()
{
std::string text;
std::getline(std::cin, text);
if (text.find("7 ") != std::string::npos)
{
if (aux()) // We dont need the variable here since we use return value of the aux
std::cout<< "A function to equal a group of words" << std::endl;
else
std::cerr << "Bad string detected" << std::endl;
}
getchar();
return 0;
}
Now it starts to work
I'm trying to make sure all arguments passed to main are valid integers, and if not, I'll print an error. For example, if I have an executable named total, I would enter total 1 2 3 4.
I want to print an error if there's an invalid integer, so if I enter total 1 2 3zy it will print an error message. My code is as follows.
#include <iostream>
#include<stdlib.h>
using namespace std;
bool legal_int(char *str);
int main(int argc, char *argv[])
{
//int total = 0;
for(int i = 1; i < argc; i++)
{
if( (legal_int(argv[i]) == true) )
{
cout << "Good to go" << endl;
}
else
{
cerr << "Error: illegal integer." << endl;
return 1;
}
}
// int value = atoi(argv[i]);
//cout << value << endl;
}
bool legal_int(char *str)
{
while(str != 0) // need to
if( (isdigit(str)) )// do something here
{
return true;
}
else
{
return false;
}
}
What I need to know is how can I index through all the characters in the string and make sure they are digits with the legal_int function?
When comparing every character, the logic should be if it's not legal, return false, otherwise continue:
bool legal_int(char *str)
{
while (str != 0)
{
if (!isdigit(*str))
{
return false;
}
str++;
}
return true;
}
What about:
bool legal_int(char *str) {
while (*str)
if (!isdigit(*str++))
return false;
return true;
}
It is not the best function but it should serve the purpose. The isdigit function needs a character to look at so pass in *str. The other key point is that you need to advance the pointer inside of the loop.
bool legal_int(char *str)
{
while(str != 0) // need to
if( (isdigit(str)) )// do something here
{
return true;
}
else
{
return false;
}
}
You have three mistakes:
while (str != 0) should be while (*str != 0). You want to continue until you encounter a zero in the string, not until the string itself goes away.
if( (isdigit(str)) ) should be if( (isdigit(*str++)) ). You want to look at what str points to and see if that's a digit, and you need to point to the next digit.
return true; That should not be there. You don't want to return just because you found a single digit.
Here is the code I used to detect the string in a line from a txt file:
int main()
{
std::ifstream file( "C:\\log.txt" );
std::string line;
while(!file.eof())
{
while( std::getline( file, line ) )
{
int found = -1;
if((found = line.find("GetSA"))>-1)
std::cout<<"We found GetSA."<<std::endl;
else if ((found = line.find("GetVol"))>-1)
std::cout<<"We found GetVol."<<std::endl;
else if ((found = line.find("GetSphereSAandVol"))>-1)
std::cout<<"We found GetSphereSAandVol."<<std::endl;
else
std::cout<<"We found nothing!"<<std::endl;
}
}
std::cin.get();
}
And here is my log file:
GetSA (3.000000)
GetVol (3.000000)
GetSphereSAandVol (3.000000)
GetVol (3.000000)
GetSphereSAandVol (3.000000)
GetSA (3.00000)
The error is, the program will not go to find "GetSphereSAandVol", because it stops at "GetSA". Obviously, the program thinks "GetSphereSAandVol" contains "GetSA", so it will execute:
if(found = line.find("GetSA"))
std::cout<<"We found GetSA."<<std::endl;
which is not exactly what I want, because I am expecting the program to execute:
else if (found = line.find("GetSphereSAandVol"))
std::cout<<"We found GetSphereSAandVol."<<std::endl;
So, anyway I can avoid this? to get what I really want? Thanks a lot.
You misunderstand how find works. Read the documentation.
The conditionals should go like this:
if ((found = line.find("xyz")) != line.npos) { /* found "xyz" */ }
I would write your entire program like this:
int main(int argc, char * argv[])
{
if (argc != 2) { std::cout << "Bad invocation\n"; return 0; }
std::ifstream infile(argv[1]);
if (!infile) { std::cout << "Bad filename '" << argv[1] << "'\n"; return 0; }
for (std::string line; std::getline(infile, line); )
{
int pos;
if ((pos = line.find("abc")) != line.npos)
{
std::cout << "Found line 'abc'\n";
continue;
}
if ((pos = line.find("xyz")) != line.npos)
{
std::cout << "Found line 'xyz'\n";
continue;
}
// ...
std::cout << "Line '" << line << "' did not match anything.\n";
}
}
Two errors, one you asked about and one you didn't.
Your if statements are wrong. You misunderstand how string::find works. This is the correct way
if ((found = line.find("GetSA")) != string::npos)
...
else if ((found = line.find("GetVol")) != string::npos)
...
etc.
If string::find does not find what it's looking for it returns a special value string::npos. This is what your if conditions should test for.
Second error, lose the while (!file.eof()) loop, it's completely unnecessary.
The string::find function returns string::npos if not found. Otherwise it returns an index. You are assuming it returns a boolean and are testing accordingly. That will not work, because string::npos evaluates to a boolean truth (non-zero). Also, if the substring is at index zero, that will not pass.
You must instead do this:
if( std::string::npos != (found = line.find("GetSA")) )
// etc...
Personally, I don't like the style of setting a value and testing in this way, but that's up to you. I might do this instead with a simple helper function:
bool FindSubString( std::string& str, const char *substr, int& pos )
{
pos = str.find(substr);
return pos != std::string::npos;
}
Then:
if( FindSubString( line, "GetSA", found ) )
// etc...
But in your case, you're not even using the found variable. So you can ignore what I've said about style and just do:
if( std::string::npos != line.find("GetSA") )
// etc...