简体   繁体   中英

How can I check if a number (double type) stored as a string is a valid double number in C++?

I'm having an issue with a program I'm working on in C++. I am asking the user to input a valid number. I take it in as a string because the particular assignment I'm doing, it makes it easier in the long run. For basic error checking, I want to check to see if the number entered is a valid number. Example:

Enter number: 3.14
This would be valid

Enter number: 3.1456.365.12
This shouldn't be valid

use strtod, which converts a string to a double and returns any characters it couldn't interpret as part of the double.

double strtod(const char* nptr, char** endptr)

Like this:

char* input = "3.1456.365.12";
char* end;

strtod(input, &end);
if (*input == '\0')
{
  printf("fail due to empty string\n");
}
if (end == input || *end != '\0')
{
  printf("fail - the following characters are not part of a double\n%s\n", end);
}

我认为boost :: lexical_cast应该可以帮助您

An example using only standard C++:

#include <sstream>

// ...

double dbl = 0.0;
std::istringstream num("3.1456.365.12");

num >> dbl;

if(!num.fail() &&
   num.eof()) // This second test is important! This makes sure that the entire string was converted to a number
{
    // success
}
else
{
    // failure
}

Bonus generic template function version:

#include <sstream>
#include <string>
#include <exception>

// Version that throws an exception on a bad parse:

template <typename T>
T parse(const std::string& str)
{
    T value;
    std::istringstream parser(str);
    parser >> value;

    if(!parser.fail() && parser.eof())
    {
        return value;
    }
    else
    {
        throw "bad lexical cast";
    }
}

// usage:

double foo = parse<double>("3.14234");

// Non-exception, error code version

template <typename T>
bool parse(const std::string& str, T& value)
{
    std::istringstream parser(str);
    parser >> value;

    return (!parser.fail() && parser.eof());
}

// usage:

double foo = 0.0;
bool success = parser<double>("3.11234", foo);

如果没有提升,则始终可以使用strtod

You can use strtoX (where X is f for float, l for long, ul for unsigned long, etc.), choosing for the kind of number you want. One of the parameters you give it is an "end pointer", which points to the first character in the string that could not be converted into the target number type.

In your case, what you're apparently looking for is that the end pointer should be at the end of the string, indicating that all characters in the string were converted to the target type.

Edit: Sorry, didn't notice that you'd mentioned 'double' in the title (but not the question itself). That being the case, you'd use strtod , as a couple of others have also advised.

The best way is to make an actual attempt to convert your string to double using any of the standard and/or idiomatic ways to do the conversion, and check for errors afterwards. In C that would be functions from strto... group (which are, of course, perfectly usable in C++ as well). In C++ you can use stream-based conversion idiom.

One thing to watch for though is that the common convention in standard conversion methods is to convert "as much as possible" and not consider any extra characters as an error. For example, a string "123abc" is normally considered valid input, with only "123" part getting converted. All usable methods provide you with the way to detect the fact that there is something extra after the actual number, if you want to treat this situation as an error. But it is up to you to take the additional steps to perform this verification.

A simple option is to use the sscanf function:

const char * num_as_str = "3.1416";
double num;

if(std::sscanf(num_as_str, "%lg", &num) == 1)
{ 
    std::cout << "You correctly entered the number " << num << "\n";
} 

If you want to get fancy you can use istringstream:

std::istringstream iss(num_as_str);
if(iss >> num)
{
    std::cout << "You correctly entered the number " << num << "\n";
}

If you want to get extra-fancy you can use boost's lexical_cast:

try
{
    num = boost::lexical_cast<double>(num_as_str);
}
catch(boost::bad_lexical_cast &)
{ 
    std::cout << "What you entered is not a proper number" << num << "\n";
}

Ah, I loved these assignments. A good old hand written lexer is the way to go (since you are still in the begining days -- don't try to use boost just yet). They are fast, easy to write and extremely fun to play with. If you can get a copy of Levine's book on Lex/Yacc, look up the first couple of chapters for ideas.

As mentioned by AndreyT, the best way is to attempt to convert the string into a float and check for an error. Personally I would opt to use std::istringstream, as you're using C++. Something like the following should work:

float ff;
std::istringstream istr;
std::string input("1234.5678");

// set the stream to have your string as its base
istr.str(input);

// now try to read the number:
istr >> ff;
if (istr.fail())
{
    // some error in the parsing
}

istringstream is part of STL, so you shouldn't need any additional libraries, and it will also with with exceptions if that's your choice. More information can be found here: http://www.cplusplus.com/reference/iostream/istringstream/

You could use regular expressions. Since you already have a string, it would be easy to compare that with this regex:

/^\d+(\.\d+)?$/

The library regex.h can help you here. See this: regex.h

This is my quick hack :)

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template <typename T>
bool fromStr(const std::string& str, T& var)
{
    std::stringstream convertor;
    convertor << str;
    convertor >> var;

    if( convertor.fail() )
        return false;

    char c = static_cast<char>( convertor.get() );

    return convertor.eof() || c == '\n' || c == ' ' || c == '\t';
}

int main()
{
    double d;
    std::string str = "5.04146.55";

    if( fromStr<double>(str, d) )
    {
        std::cout << "Valid conversion!, d = " << d;
    }
    else
    {
        std::cout << "Invalid conversion!";
    }   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM