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?
CodePudding user response:
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.
CodePudding user response:
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;
}