tags:

views:

1262

answers:

4

I have a std::string which could be a string or could be a value (such as 0) Whats the best or easiest way to convert the string to int with the ability to fail? i want a c++ version of C#'s Int32.TryParse

+24  A: 

Use boost::lexical_cast. If the cast cannot be done, it will throw an exception.

#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>

int main(void)
{
    std::string s;
    std::cin >> s;

    try
    {
        int i = boost::lexical_cast<int>(s);

        /* ... */
    }
    catch(...)
    {
        /* ... */
    }
}


Without boost:

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

int main(void)
{
    std::string s;
    std::cin >> s;

    try
    {
        std::stringstream ss(s);

        int i;
        if ((ss >> i).fail() || !(ss >> std::ws).eof())
        {
            throw std::bad_cast();
        }

        /* ... */
    }
    catch(...)
    {
        /* ... */
    }
}


Faking boost:

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

template <typename T>
T lexical_cast(const std::string& s)
{
    std::stringstream ss(s);

    T result;
    if ((ss >> result).fail() || !(ss >> std::ws).eof())
    {
        throw std::bad_cast();
    }

    return result;
}

int main(void)
{
    std::string s;
    std::cin >> s;

    try
    {
        int i = lexical_cast<int>(s);

        /* ... */
    }
    catch(...)
    {
        /* ... */
    }
}


If you want no-throw versions of these functions, you'll have to catch the appropriate exceptions (I don't think boost::lexical_cast provides a no-throw version), something like this:

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

template <typename T>
T lexical_cast(const std::string& s)
{
    std::stringstream ss(s);

    T result;
    if ((ss >> result).fail() || !(ss >> std::ws).eof())
    {
     throw std::bad_cast();
    }

    return result;
}

template <typename T>
bool lexical_cast(const std::string& s, T& t)
{
    try
    {
     // code-reuse! you could wrap
     // boost::lexical_cast up like
     // this as well
     t = lexical_cast<T>(s);

     return true;
    }
    catch (const std::bad_cast& e)
    {
     return false;
    }
}

int main(void)
{
    std::string s;
    std::cin >> s;

    int i;
    if (!lexical_cast(s, i))
    {
     std::cout << "Bad cast." << std::endl;
    } 
}
GMan
+1 for 3 ways to do it
Daniel
This will succeed even if the string contains invalid characters after a valid number e.g. "123abc"
Phil Devaney
I do the same (using boost::lexical_cast) although throwing an exception if the conversion will regularly fail bothers me from a performance POV.
Rob
I *think* I solved the extra padding issue, but if someone can confirm that this is the way to do it, please do.
GMan
@GMan: This works OK for me, though an easier way to check if you've reached the end might be to call eof() on the stream. Your 'Without boost' version has a redeclaration of i in it.@Rob: I agree about the exception, if you are validating user input where you expect the cast to regularly fail it would be better to write a version of the function that returns a bool and the result in an out parameter, like .NET's TryParse methods.
Phil Devaney
Indeed, that's much better. Thanks :)
GMan
While trailing whitespace might be seen as an error, IME usually it isn't. So changing `ss >> i` to `ss >> i >> std::ws` might help to avoid some silly error cases.
sbi
Alright, how's that? :)
GMan
Looks good to me. :)
sbi
@GMan Thank you for showing `boost:lexical_cast` :D
Elpezmuerto
+4  A: 

Another way using standard streams :

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

int main()
{
    std::stringstream convertor;
    std::string numberString = "Not a number!";
    int number;

    convertor << numberString;
    convertor >> number;

    if(convertor.fail())
    {
     // numberString is not a number!
     std::cout << "Not a Number!";
    }
}
AraK
You could create a nice general `stream_cast` using this. However, this would need a few specializations -- namely for streaming from and into strings, as the above is likely wasting time streaming the string into the stream, instead of initializing the it.
sbi
that looks so weird... I'm not sure I can even read that code. that puts the number in `number`? looks somehow dirty.
Mark
This is actually what boost::lexical_cast does internally.
Billy ONeal
+4  A: 

The other answers that use streams will succeed even if the string contains invalid characters after a valid number e.g. "123abc". I'm not familiar with boost, so can't comment on its behavior.

If you want to know if the string contains a number and only a number, you have to use strtol:

#include <iostream>
#include <string>

int main(void)
{
    std::string s;
    std::cin >> s;

    char *end;
    long i = strtol( s.c_str(), &end, 10 );
    if ( *end == '\0' )
    {
     // Success
    }
    else
    {
     // Failure
    }
}

strtol returns a pointer to the character that ended the parse, so you can easily check if the entire string was parsed.

Note that strtol returns a long not an int, but depending on your compiler these are probably the same. There is no strtoi function in the standard library, only atoi, which doesn't return the parse ending character.

Phil Devaney
No, I've created a version that solves the problems, using streams, and generically.
GMan
+1 for strtol. Streams are too heavy for simple conversion.
Kirill V. Lyadvinsky
I'd accept this as a specialization of the `lexical_cast` template, but otherwise I'd prefer the general streaming implementation.
sbi
+1 streams are really heavy KISS ftw.
0xC0DEFACE
A: 

Before boost's lexical_cast was available, I used to do the following:

namespace detail {

    template< typename Target, typename Source >
    struct stream_caster {
     static Target stream_cast(const Source& s)
     {
      std::stringstream ss;
      if( (ss << s).fail() ) {
       throw std::bad_cast("could not stream from source");
      }
      Target t;
      if( (ss >> t).fail() || !(ss >> ws).eof) {
       throw std::bad_cast("could not stream to target");
      }
      return t;
     }
    };

    template< typename T >
    struct stream_caster<T,T> {
     static const T& stream_cast(const T& s)
     {
      return s;
     }
    };

    template< typename Source >
    struct stream_caster<std::string,Source> {
     static std::string stream_cast(const Source& s)
     {
      std::ostringstream oss;
      if( (oss << s).fail() ) {
       throw std::bad_cast("could not stream from source");
      }
      return oss.str();
     }
    };

    template< typename Target >
    struct stream_caster<Target,std::string> {
     static Target stream_cast(const std::string& s)
     {
      std::stringstream ss(s);
      Target t;
      if( (ss >> t).fail() || !(ss >> ws).eof) {
       throw std::bad_cast("could not stream to target");
      }
      return t;
     }
    };

    template<>
    struct stream_caster<std::string,std::string> {
     static const std::string& stream_cast(const std::string& s)
     {
      return s;
     }
    };

}

template< typename Target, typename Source >
inline Target stream_cast(const Source& s)
{
    return detail::stream_caster<Target,Source>::stream_cast(s);
}
sbi