5

The following code converts an std::string to int and the problem lies with the fact that it cannot discern from a true integer or just a random string. Is there a systematic method for dealing with such a problem?

#include <cstring>
#include <iostream>
#include <sstream>

int main()
{
    std::string str =  "H";

    int int_value;
    std::istringstream ss(str);
    ss >> int_value;

    std::cout<<int_value<<std::endl;

    return 0;
}

EDIT: This is the solution that I liked because it is very minimal and elegant! It doesn't work for negative numbers but I only needed positive ones anyways.

#include <cstring>
#include <iostream>
#include <sstream>

int main()
{
    std::string str =  "2147483647";

    int int_value;
    std::istringstream ss(str);

    if (ss >> int_value)
        std::cout << "Hooray!" << std::endl;

    std::cout<<int_value<<std::endl;


    str =  "-2147483648";
    std::istringstream negative_ss(str);

    if (ss >> int_value)
        std::cout << "Hooray!" << std::endl;

    std::cout<<int_value<<std::endl;

    return 0;
}
pandoragami
  • 5,387
  • 15
  • 68
  • 116
  • 3
    `if (ss >> int_value)` might be a good start. – WhozCraig Apr 24 '13 at 00:56
  • @WhozCraig not sure what you mean, can you please elaborate? – pandoragami Apr 24 '13 at 00:56
  • 1
    *Try it.* Replace your current bare `ss >> int_value;` with `if (ss >> int_value) std::cout << "Hooray!" << std::endl;` – WhozCraig Apr 24 '13 at 00:58
  • Oh ok, you should at least answer the question then. – pandoragami Apr 24 '13 at 01:00
  • Some information on the [string stream operator](http://en.cppreference.com/w/cpp/io/basic_ios/operator_bool) used in WhozCraig's suggestion. – Captain Obvlious Apr 24 '13 at 01:10
  • you should note, it *will* skip leading whitespace if present, to reach the object if its desire, so `"10"`, `"-100"`, and `" -42"`, will all work. If that is your choice of poison, its a pretty robust mechanism. If you want failure on anything but pure numerics (with optional sign) *including* failure on leading whitespace, its not a good fit, or at least not without some prep. – WhozCraig Apr 24 '13 at 01:12
  • 1
    hmm, sounds familiar, you may want to see http://stackoverflow.com/questions/16156881/checking-to-make-sure-argv1-is-an-integer-c, and my solution http://stackoverflow.com/questions/16156881/checking-to-make-sure-argv1-is-an-integer-c/16156999#16156999 – Arun Apr 24 '13 at 01:17
  • @Arun - No because I'm using an `std::istringstream` object for doing it and its ten times simpler than anything else shown so far. WhozCraigs' answer wins the thread by the way. I edited my post for the solution which I liked. thanks anyways though. – pandoragami Apr 24 '13 at 10:34

3 Answers3

7

You may try to use Boost lexical_cast, it will throw an exception if the cast failed.

int number;
try
{
     number = boost::lexical_cast<int>(str);
}
catch(boost::bad_lexical_cast& e)
{
    std::cout << str << "isn't an integer number" << std::endl;
}

EDIT Accorinding to @chris, You may also try to use std::stoi since C++11. It will throw std::invalid_argument exception if no conversion could be performed. You may find more information here: std::stoi

taocp
  • 23,276
  • 10
  • 49
  • 62
  • thats nice of you tacp but WhozCraig answered it in a very simple way. thanks though. – pandoragami Apr 24 '13 at 01:05
  • @WhozCraig yeah, the first way will miss negative numbers, very nice catch, thank you very much for that. I updated it to point that out. – taocp Apr 24 '13 at 01:07
  • @lost_with_coding you are welcome, but as WhozCraig pointed out, the first way will not work when string is actually a negative number. so better to use the boost lexical_cast way. – taocp Apr 24 '13 at 01:08
  • @tacp yeah, I only noticed because I've long-lost-count of how many times I've done the same damn thing. (sigh) – WhozCraig Apr 24 '13 at 01:09
  • 1
    The concept is correct. The loop, to me, does not look like idiomatic C++ – Arun Apr 24 '13 at 01:19
  • 1
    By the way, there's a similar function for this purpose in the standard library, `std::stoi`, but it is slightly less strict, and throws two or three different exceptions. – chris Apr 24 '13 at 02:11
5

WhozCraig's approach is much nicer and I wanted to expand on it using the approach that the C++ FAQ uses which is as follows:

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

class BadConversion : public std::runtime_error {
public:
  BadConversion(std::string const& s)
    : std::runtime_error(s)
    { }
};



inline int convertToInt(std::string const& s,
                              bool failIfLeftoverChars = true)
{
  std::istringstream i(s);
  int x;
  char c;
  if (!(i >> x) || (failIfLeftoverChars && i.get(c)))
    throw BadConversion("convertToInt(\"" + s + "\")");
  return x;
}


int main()
{
    std::cout << convertToInt( "100" ) << std::endl ;
    std::cout << convertToInt( "-100" ) << std::endl ;
    std::cout << convertToInt( "  -100" ) << std::endl ;
    std::cout << convertToInt( "  -100  ", false ) << std::endl ;

    // The next two will fail
    std::cout << convertToInt( "  -100  ", true ) << std::endl ;
    std::cout << convertToInt( "H" ) << std::endl ;
}

This is robust and will know if the conversion fails, you also can optionally choose to fail on left over characters.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I think I made something like this at one point, though a bit more general, and offered something specific that `std::stoi` and `boost::lexical_cast` did not. It was something like `if (SafeInput::Input("Enter an integer: ", someInt, SafeInput::Options::NoLeftovers)) {...}` :p – chris Apr 24 '13 at 02:16
1
/* isdigit example */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main ()
{
  char str[]="1776ad";
  int year;
  if (isdigit(str[0]))
  {
    year = atoi (str);
    printf ("The year that followed %d was %d.\n",year,year+1);
  }
  return 0;
}