user2953119
user2953119

Reputation:

How to detect if the conversion failed in c++0x

I tried to perform a string to int conversion as follows:

#include<iostream>
#include<sstream>
#include<string>

int main(int argc, char ** argv)
{
    int i;
    char *str = "12a3";
    std::stringstream ss;
    ss << str;
    ss >> i;
    std::cout << i << std::endl; //prints 12
}

DEMO

But the resul wasn't one that I expected. I'm a Java so honestly I was waiting for some exception to be thrown. But it wasn't. Is it possible to perform such casting in a safe-way without the legacy atoi function.

Note, that I'm in C++0x therefor cannot use std::stoi.

Upvotes: 0

Views: 457

Answers (5)

Dale Wilson
Dale Wilson

Reputation: 9434

In the comments we concluded that your goal is to determine whether a string consists solely of a valid representation of a number.

C, and by extension C++ has always focused on stream I/O and therefore stream-like numeric conversions. The consequence of that is that scanf, istream >>, atoi, and strol all stop at the first character that is NOT part of a valid number.

You goal then is to determine whether the input is fully consumed, or if there are trailing "garbage" characters. The simplest approach is to use strtol which will return a pointer to the first unconsumed character.

char * endptr;
long i = strtol (str, &endptr, 10); 
bool valid = *endptr == '\0';

This will still fail if your input looks like this: " 123 ", because the scan will stop at the space character following the '3'.

If your requirements are that trailing spaces are acceptable, your code will need to examining the string that begins at *endptr after the call, to determin if it is an acceptable suffix, or it could trim the input string before doing the test.

Upvotes: 0

greyfade
greyfade

Reputation: 25647

When the operator >> finds a result that it can't use, it sets failbit on the stream, and the stream itself will evaluate as false:

if(!(ss >> i)) {  // or ss >> i; if(!ss) or if(ss.fail())
    // the conversion failed, do whatever you need to here.
    ss.clear(); // reset the fail flag so you can read from the stream again

    // you can use ss.ignore() to skip bad input if you want to move on to other parts
}

Upvotes: -1

Derek T. Jones
Derek T. Jones

Reputation: 1822

You need to test both unsuccessful stream reading and incomplete stream reading:

ss >> i;
if (!ss || !ss.eof()) {
  throw std::invalid_argument("Could not convert to int");
}

Upvotes: 0

user4581301
user4581301

Reputation: 33931

Simplest approach I know of.

int main(int argc, char ** argv)
{
    std::string num;
    long i;
    char *str = "12a3";
    char * endp;
    std::stringstream ss;
    ss << str;
    ss >> num;
    i = strtol(num.c_str(), // string to convert as a C style string, 
               &endp, //pointer to be updated with character that ended the conversion
               10); //base of number conversion
    if (*endp != '\0')
    {
        //didn't read whole string. conversion failed
    }
    std::cout << i << std::endl; //prints 12
}

Upvotes: 0

Aaron McDaid
Aaron McDaid

Reputation: 27133

This is the correct behaviour. The operation to >> into an int succeeded, and this is correct.

You have to ask a different question. The test you really want to perform is: "after I successfully read an int, are there any more characters which are leftover in the stream and have not yet been processed?"

Consider cin. When you do

int x, y;
std::cin >> x;
std::cin >> y;

it reads as much as it can from standard input into x, stopping at the first non-digit. Then, if there is just some whitespace after the first number, then it will attempt to read a number after the whitespace and store that number in y.

All input streams (whether cin or an instance stringstream) are expected to contain many strings/numbers/whatever. Each >> simply reads as much as it can (successfully).

Upvotes: 4

Related Questions