开发者

C++ String to Double parsing with exceptions?

开发者 https://www.devze.com 2023-01-14 15:41 出处:网络
In java, if I wanted to create some application which could receive both doubles and strings as appropriate input, I would probably do the following:

In java, if I wanted to create some application which could receive both doubles and strings as appropriate input, I would probably do the following:

String input = getInput();//
try { 
    double foo = Double.valueOf(input);
    //Do stuff with foo here 
} catch (NumberFormatException e) { 
    //Do other validation with input
} 

How would you go about doing that in c++? atof() returns 0.0 for invalid input, but how would you differentiate that from a valid double of "0.0"? As an aside, I can only include <iostream>, <string>, <cstdlib>, and <cassert> in this project. I'm assuming I need to use cin in some way, but how can you grab the original input after cin fails to parse some string as a double?

Edit: I could probably use the following, but as I said before, I'm not allowed to import <sstream> on this assignment for some reason

开发者_运维百科
string input;
getline(cin, input);

double x;
istringstream foo(input);
foo >> x
if(cin){
    //do manipulations with x
}
else{
    //since it's not a number, check if input is a valid command etc..
}


Exceptions should be reserved for exceptional situations. While you certainly can abuse them like this, it's a lousy idea -- clearly you're pretty much expecting things other than doubles, so treating it as an exception doesn't make any real sense.

The only real question is the exact circumstance under which you want the input treated as a string. Just for example, if the input string was something like "1.23e2 non-numeric data", do you want to just use the "1.23e2" as a number, or do you want to treat the whole thing as a string?

Either way, you'd want to us strtod for the conversion -- the difference is only how you react to what it returns. In particular, strtod takes two parameters instead of just one like atof does. The second parameter is a pointer to pointer to char. Assuming you pass a non-NULL pointer, strtod will set it to point at the last character in the input string that it successfully converted. If it's pointing to the beginning of the input string, nothing was converted. If it's pointing to the end, everything was converted. If it's somewhere in between, part of the string converted, and part didn't.

For the moment, I'm going to assume that you want a double value holding whatever number could be converted at the beginning of the string, and whatever couldn't be converted treated as a string:

#include <stdlib.h>
#include <stdio.h>

int main() { 
    char input[] = "12.34 non-numeric data";
    char *string;
    double value = strtod(input, &string);

    printf("Number: %f\tstring: %s\n", value, string);
    return 0;
}


C++ streams associate the good, bad and fail state flags. These are stored in ios_base::goodbit, ios_base::badbit and ios_base::failbit respectively, but are commonly accessed through ios::good(), ios::bad() and ios::fail(). There's also ios_base::eofbit and ios::eof() but let's ignore that for the moment.

If parsing fails then the bad bit raises. Also, stream objects are convertible to a boolean-compatible type. If a stream is in a good state, then the statement

if( stream )

evaluates stream as true. Otherwise, it evaluates it as false. With this at hand, grabbing a value from standard input boils down to this:

#include <iostream>

// ...
double d = 0;
if ( std::cin >> d ) {
    // input is a double. handle that here.
} else {
    // input isn't a double. handle that here.
}


try "strtod" in stdlib.h


The C++ standard library seems to avoid exceptions for a lot of things that people might expect them, and this may not be the answer that you want to hear but parsing doubles isn't exactly rocket science so maybe it'd be ok to do it "by hand"?

One note if you do this, you'll probably want to collect it into an integer and then convert to a double and divide by 10^number_of_digits_after_decimal_point.


You could have a look at boost::lexical_cast, which would allow you to write pretty much the equivalent of the Java code:

string input;
getline(cin, input);

try {
    double x = boost::lexical_cast<double>(input);
    //Do manipulations with x
} catch (boost::bad_lexical_cast &) {
    //Do other validation with input
}
0

精彩评论

暂无评论...
验证码 换一张
取 消