0

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?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Upgrade
  • 95
  • 6
  • Reading about the functions you're using helps. If the first thing they encounter is not a digit or sign, they throw. You'll have to try/catch. I also feel that checking for two integer types is redundant. – sweenish Dec 15 '21 at 09:28
  • But I keep reading that try/catch is not a good practice in C++ – Upgrade Dec 15 '21 at 09:29
  • 1
    check this link it seems the same to your problem https://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c – user10620381 Dec 15 '21 at 09:30
  • possible duplicate: https://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c – OrenIshShalom Dec 15 '21 at 09:31
  • 1
    "But I keep reading that try/catch is not a good practice in C++" I don't know where you read that. That is indeed true, but for very specific narrow use cases like embedded, or maybe some very low level game engine stuff. And even then the solution is to disable extensions all together, having exceptions but avoiding `try/catch` accomplishes nothing. For general use, exceptions are ok. – bolov Dec 15 '21 at 09:32
  • `strtol` and famility is useually used for this. – bolov Dec 15 '21 at 09:34
  • @Upgrade: When reading that try / catch is not good practice do they go on to explain themselves? If it's something to do with overhead, then that's balderdash in your case since the performance bottleneck will be all the file I/O. – Bathsheba Dec 15 '21 at 09:57

2 Answers2

2

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

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;
}
OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87