Home > Mobile >  Check if a string is a number without regex or try catch
Check if a string is a number without regex or try catch

Time:12-15

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;
}
  •  Tags:  
  • c
  • Related