2

I'm trying to convert a numeric std::string to an integer using std::istringstream

std::istringstream convertor;

convertor.str(mystring);
convertor >> myint;

I want to throw an exception if the numeric string is over (or under) the limits of an integer, but I don't know what is the best way to do so. I wonder if there is something specific to std::istringstream or something else made especially for this purpose, or do I have to use dirty (it seems dirty at least :) ) ways with numeric_limits ?

Thank you for your help.

Julien Fouilhé
  • 2,583
  • 3
  • 30
  • 56
  • 1
    `basic_ios` can be set up to indicate the various categories of error by setting an internal flag (which you must then check) *or* by throwing an exception. http://en.cppreference.com/w/cpp/io/basic_ios/exceptions – BoBTFish Feb 19 '13 at 15:26
  • 3
    `std::stoi` does this automatically. – chris Feb 19 '13 at 15:29
  • Look at [this example](http://liveworkspace.org/code/vhB0T$0). – BoBTFish Feb 19 '13 at 15:31
  • You may find this interesting http://stackoverflow.com/questions/13045693/negative-numeric-string-e-g-10-to-unsigned-short – hmjd Feb 19 '13 at 15:32
  • possible duplicate of [Validate string within short range](http://stackoverflow.com/questions/5834439/validate-string-within-short-range) – Robᵩ Feb 19 '13 at 15:36
  • Do you need to partially convert an input or not ? By that I mean, does your input solely represent a number (`"12345"`) or may the number be followed by something else entirely (`"12345foo"`) ? It's important to assert whether you should still be able to read `foo` afterward in the latter case or whether it's an eroneous input; as this changes which solutions are acceptable. – Matthieu M. Feb 19 '13 at 15:43
  • If you like overkill, you could use a bignum library like this one http://www.boost.org/doc/libs/1_53_0/libs/spirit/classic/doc/numerics.html and then convert the arbitrary integer back to your favorite int type. – Christian Feb 19 '13 at 16:10
  • My input is always valid. In fact, I have to parse an asm file with a given grammar and my function always receives a valid string, except the number can be higher or lower than limits of the type... I just updated my message to show you a strange bug I have with your answers. – Julien Fouilhé Feb 19 '13 at 16:11

3 Answers3

2
std::istringstream convertor;
convertor.str(mystring);
if(!(convertor >> myint)) {
    thow std::invalid_argument();
}

It should be noted such a function is already available in the standard library. (C++11)

it=std::stoi(my_string);

http://en.cppreference.com/w/cpp/string/basic_string/stol

Furthermore this is arguably this is exception abuse as a precondition to the function is that mystring should represent a integer value, and if that is not a precondition then it is a part of the functions normal flow control to handle the case where it is not.

I would probably write:

std::pair<bool, int> try_to_int(const std::string& str);

Or a boost::optional would work well here too

111111
  • 15,686
  • 6
  • 47
  • 62
  • Only if you have C++11 (which isn't the case of most people.) – James Kanze Feb 19 '13 at 15:29
  • @JamesKanze "which isn't the case of most people" says who? Also the doc I linked also mentions that fact. – 111111 Feb 19 '13 at 15:32
  • I wonder @111111 what is in your (or the standards) opinion the correct way to handle invalid input? Just do some undefined behaviour? – Jonas Schäfer Feb 19 '13 at 15:36
  • `out_of_range` is a fishy thing to throw if the stream contains `"quack"`, which is why `stoi` can also throw `invalid_argument`. – aschepler Feb 19 '13 at 15:37
  • @JonasWielicki I don't think the standard says much on the matter, I personally write asserts for invalid input, because if I give invalid input to a function then that IS a bug. – 111111 Feb 19 '13 at 15:55
  • @111111 Says reality. Most compilers don't support C++11, and those that do don't support it completely or well. And very few people can use the latest version of the compiler; most companies won't want to use a new major version until there've been a few patch releases. (There business isn't testing compilers.) – James Kanze Feb 19 '13 at 15:55
  • @aschepler yes, you are right, I was crudely copying that from the stoi doc. I guess std::invalid_argument is slightly better. As I said, I would make a try function anyway. – 111111 Feb 19 '13 at 15:56
  • @JonasWielicki anyway, if you wanted your function to discern the input, that is when I start using "try" functions which turn a tuple, pair of optional type. – 111111 Feb 19 '13 at 15:58
  • @JamesKanze All the mainstream compilers have enough C++11 implemented to use std::stoi. If someone is constrained by workplace requirements then they would make that clear in their question. The worst case is that they try it and it doesn't work. – 111111 Feb 19 '13 at 16:03
  • @111111 So you basically test twice, once in the try-function and once in the function which does the actual convert (inside of an assert, which can be removed at compile-time if desired)? – Jonas Schäfer Feb 19 '13 at 16:05
  • @JulienFouilhé which method, I mentioned a few? (or do you mean all -- because `std::istringstream` is less a part of the standard than `std::invalid_argument`. – 111111 Feb 19 '13 at 16:10
  • I meant std::stoi. The one with invalid_argument displays the same error I added to my message. Thanks for your answer. – Julien Fouilhé Feb 19 '13 at 16:13
  • @111111 Not everyone can use the latest version of the compiler (and I don't have access to all of the "mainstream" compilers to know, but I have had problems with C++11 support with the version of g++ I use). As a general rule, it's best to assume that the poster can't count on C++11 unless he says so, as that's the reality in the working world. – James Kanze Feb 19 '13 at 16:16
  • @JamesKanze I disagree, I do and will continue to give information out according to the *current* standard and what the current mainstream compilers and stdlib have implemented. Unless there is some SO reason not to or the OP has said that this is not possible. – 111111 Feb 19 '13 at 16:18
1

If you have access to C++11 features, try std::stoi(my_string);

If you do not, but have access to boost libraries, try:

std::string str("112211"); 
int x = 0;
try
{
    x = boost::lexical_cast<int>(str);
}
(catch boost::bad_lexical_cast&)
{
}

boost::lexical cast is defined (for the generic case at least), to use an istringstream internally (If you override std::istream& operator>> for your_type, you will be able to read your type with lexical_cast<your_type>(string));

If you do not have access to boost either, roll your own (others in this thread have already pointed out how to set iostreams to throw exceptions).

edit: rolling your own:

template<typename T>
T lexical_cast<T>(const std::string & str)
{
    std::istringstream buffer(str);
    buffer.exceptions(std::istringstream::failbit);
    T retval;
    buffer >> retval;
    return retval;
}

You can particularize this for value types that do not support iostream io.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
0

Thank you for your answers, I found the solution in the comments of my question:

std::istringstream convertor;

convertor.str("16");
convertor >> myint;
if (convertor.fail())
    std::cerr << "error" << std::endl;

Or, to throw an exception:

std::istringstream convertor;

convertor.exceptions(std::istringstream::failbit);
convertor.str("16");
convertor >> myint;
Julien Fouilhé
  • 2,583
  • 3
  • 30
  • 56