Преобразуйте строку в int с помощью bool / fail в C ++ - PullRequest
20 голосов
/ 07 августа 2009

У меня есть std::string, который может быть строкой или значением (например, 0).

Какой самый лучший или самый простой способ конвертировать std::string в int с возможностью сбоя? Я хочу C ++ версию C # Int32.TryParse.

Ответы [ 5 ]

42 голосов
/ 07 августа 2009

Используйте boost :: lexical_cast . Если приведение не может быть выполнено, оно сгенерирует исключение .

#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(...)
    {
        /* ... */
    }
}

Без повышения:

#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(...)
    {
        /* ... */
    }
}

Поддельное усиление:

#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(...)
    {
        /* ... */
    }
}

Если вам нужны версии без бросков этих функций, вам придется перехватывать соответствующие исключения (я не думаю, что boost::lexical_cast предоставляет версию без бросков), что-то вроде этого:

#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;
    }   
}
9 голосов
/ 07 августа 2009

Другие ответы, использующие потоки, будут успешными, даже если строка содержит недопустимые символы после действительного числа, например, "123abc". Я не знаком с бустом, поэтому не могу комментировать его поведение.

Если вы хотите знать, содержит ли строка число и только число, вы должны использовать 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 возвращает указатель на символ, который закончил синтаксический анализ, поэтому вы можете легко проверить, была ли проанализирована вся строка.

Обратите внимание, что strtol возвращает long, а не int, но в зависимости от вашего компилятора они, вероятно, совпадают. В стандартной библиотеке нет функции strtoi, только atoi, которая не возвращает символ конца разбора.

7 голосов
/ 09 октября 2015

Исключения не должны использоваться для логических тестов

Принятый ответ на самом деле является ужасным ответом на заданный вопрос, поскольку он нарушает заповедь «использовать исключения в исключительных случаях».

Исключения являются отличным инструментом для обработки исключительных случаев - случаев, когда что-то действительно пошло не так. Они плохие инструменты для существующих сценариев использования. Отчасти потому, что выбрасывание и отлов исключений обходятся дорого, а отчасти потому, что они вводят в заблуждение код - когда разработчик видит исключение, он должен разумно предполагать, что что-то идет не так. Хорошие обсуждения этого базового принципа имеются в большом количестве, но мне нравятся «Прагматичные программисты», или это неплохо: http://www.lohmy.de/2013/03/06/writing-use-cases-exception-or-alternate-flow/

Используйте boost :: lexical_cast, если вы всегда ожидаете число

boost :: lexical_cast - это оптимальное решение, когда оно действительно является исключением для получения не числа.

Используйте boost :: try_lexical_convert, если не-числа являются частью вашего варианта использования

Если вы просматриваете строку и хотите сделать одно, если это число, и другое, если это число, не используйте исключение для логического теста . Это просто плохое программирование.

Фактически, Boost предлагает try_lexical_convert, который используется в реализации lexical_cast (взято из документации здесь: http://www.boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/synopsis.html#boost_lexical_cast.synopsis.lexical_cast).

template <typename Target, typename Source>
    inline Target lexical_cast(const Source &arg)
{
    Target result;

    if (!conversion::try_lexical_convert(arg, result))
        throw bad_lexical_cast();

    return result;
}
7 голосов
/ 07 августа 2009

Другой способ использования стандартных потоков:

#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!";
    }
}
2 голосов
/ 07 августа 2009

До того как буст lexical_cast был доступен, я делал следующее:

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);
}
...