3

I am currently trying to accept only numeric input from the user in my program. I have done a lot of research but all the example code given can still accept the input such as 10abc.

This is a feet to meter converter.

include <iostream>
using namespace std;

bool isNumber(string feet){
    for (int i = 0; i < feet.length(); i++){
        if (isdigit(feet[i]) == false){
            return false;
        }
    return true;
    }
}

int main(){
    string feet;
    beginning:
    cout << "Enter the feet: ";
    cin >> feet;

    if (isNumber(feet))
        cout << "The meter is: " << (double)stod(feet) * 0.3048 << endl;
    else {
        cout << "Invalid input. Please try again" << endl;
        goto beginning;
    }
    return 0;
}

This code is close to perfect but it doesn't accept decimal point (speechless face) because decimal point will ruin "isdigit" function.

So, is there any method that only accept pure numeric and also include decimal point?

7
  • 1
    Do not use using namespace std as it is bad practice. Commented Jul 27, 2021 at 12:03
  • 1
    If you use it correctly, std::stod will tell you where it stopped converting characters. If that's not at the end of the input string, then the input had extraneous character at the end. Commented Jul 27, 2021 at 12:06
  • related/dupe: stackoverflow.com/questions/10828937/… Commented Jul 27, 2021 at 12:08
  • @Pete Becker yea my editor is showing me an error at "stod" just now and I understand now, thank you. Commented Jul 27, 2021 at 12:11
  • Side note: Writing isdigit(feet[i]) == false is dangerous. Although that expression will probably work, I don't recommend using it, because it implies that isdigit(feet[i]) == true will also work as intended. However, that expression won't work as intended, because idigit is not guaranteed to return either 0 or 1, but it may also return any nonzero value (for example 20) to indicate a digit. But the expression 20 == true will evaluate to false. Therefore, I recommend that you never write == false or == true when using isdigit, as its return type is int, not bool. Commented Jul 27, 2021 at 12:15

2 Answers 2

3

So, is there any method that only accept pure numeric and also include decimal point?

The function std::strspn will return the length of the string that contains only characters that are found in another string. So, if you want to allow both decimal digits and decimal points, you could use the following for input validation:

bool isNumber( string feet )
{
    return strspn( feet.c_str(), "0123456789." ) == feet.length();
}

However, as already pointed out in the comments section, the conversion function std::stod itself provides information that can be used for input validation. If you provide a second parameter to that function, it will write to the provided variable the number of characters that were matched.

The function std::stod automatically consumes all leading whitespace characters (e.g. space and tab characters) before converting the value. If you also want to also allow trailing whitespace characters, but no other types of characters, then you may want to use the following code:

std::size pos;
double d;

//attempt the conversion
d = stod( feet, &pos );

//make sure that at least one character was converted
if ( pos == 0 )
    throw "Input was invalid!";

//make sure that all remaining characters are whitespace
while ( feet[pos] != '\0' )
{
    if ( !isspace( (unsigned char)feet[pos] ) )
        throw "Input was invalid!";
    pos++;
}

If you don't want to allow any whitespace at all, including leading whitespace, then you will have to validate the string contents yourself before passing it to std::stod.

You are probably wondering why I am casting the char to unsigned char. See this question for an explanation of the reason why this is necessary. For the same reason, the following line in your code is wrong:

if (isdigit(feet[i]) == false){

It should be changed to:

if (isdigit((unsigned char)feet[i]) == false){

Also, the following problems in your code seem worth mentioning:


The line

cin >> feet;

will read a single word of input. So if the user enters for example 2318 sijslnej, then that line of code will only write 2318 into feet. I doubt that this is what you want. You may want to use feet.getline instead, as that will read the entire line instead of only the first word.


I strongly suggest that you get out of the habit of writing this:

if ( isdigit(...) == false )

Although this line will always work, it is still a very bad habit, as this habit will also cause you to write the following:

if ( isdigit(...) == true )

This line is equivalent to

if ( isdigit(...) == 1 )

which is wrong. The function std::isdigit returns a value of type int, not bool, and the return value is not guaranteed to be 0 or 1. It can return any nonzero value to indicate a digit, for example 20. In that case, the if conditional expression mentioned above will be equivalent to 20 == 1, which will evaluate to false. This is not what you want.

Therefore, instead of

if ( isdigit(...) == false )

you should write

if ( !isdigit(...) )

and instead of

if ( isdigit(...) == true )

you should write:

if ( isdigit(...) )

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so much for giving such a detail answer to me. I can feel it is a long journey to master C++ compared to other language.
@HonWiCong: Yes, C++ is a rather complicated language. But it is very powerful.
0
template <typename T>
T readFromLine(std::istream& in)
{
    std::string line;
    while (std::getline(in, line)) { // read whole line until it is possible
        std::istringstream line_stream { line }; // initialize string stream to parse line read
        T x;

        // try read `x` then discard white spaces then check if end of lien was reached
        if (line_stream >> x >> std::ws && line_stream.eof()) {
            // stop since reading was successful and line do not contain anything extra
            return x;
        }
        // continue if reading `x` was unsuccessful or when line contains something extra
    }
    // while loop ended since end of file was reached or error on input stream
    throw std::invalid_argument { "stream to short" };
}

https://godbolt.org/z/75efEP9Y8

2 Comments

While this works, it would be a lot better if you explained it. This will go over a lot of beginner heads.
honestly, I don't understand at all :3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.