22

我有一个std::string它可以是一个字符串或一个值(例如0)。

将失败转换std::stringint具有失败能力的最佳或最简单的方法是什么?我想要 C# 的 C++ 版本Int32.TryParse

4

5 回答 5

43

使用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;
    }   
}
于 2009-08-07T07:31:28.787 回答
9

即使字符串在有效数字后包含无效字符,使用流的其他答案也会成功,例如“123abc”。我不熟悉 boost,所以不能评论它的行为。

如果你想知道字符串是否包含一个数字并且只有一个数字,你必须使用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,它不返回解析结束字符。

于 2009-08-07T08:10:07.147 回答
9

异常不应用于布尔测试

对于所提出的问题,接受的答案确实是一个糟糕的答案,因为它违反了“对例外情况使用例外”的规则。

异常是处理异常情况的极好工具——那些确实出现问题的情况。对于现有用例,它们是糟糕的工具。部分原因是抛出和捕获异常代价高昂,部分原因是它会误导代码——当开发人员看到异常时,他们应该能够合理地假设那里出了问题。关于这个基本原则的很好的讨论比比皆是,但我喜欢“实用程序员”的,或者这还不错:http ://www.lohmy.de/2013/03/06/writing-use-cases-exception-或交替流/

如果您总是期望一个数字,请使用 boost::lexical_cast

boost::lexical_cast 是一个最佳解决方案,因为它确实是一个例外,因为它接收到一个非数字。

如果非数字是您的用例的一部分,请使用 boost::try_lexical_convert

如果您正在处理一个字符串并且想要在它是一个数字时做一件事,如果它是一个数字则做另一件事,不要对 boolean test 使用异常。那只是糟糕的编程。

事实上,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。概要.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;
}
于 2015-10-09T12:16:58.737 回答
8

使用标准流的另一种方法:

#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!";
    }
}
于 2009-08-07T07:34:36.657 回答
3

在 boostlexical_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);
}
于 2009-08-07T09:10:25.950 回答