Tengo un std::string que podría ser una cadena o podría ser un valor (como 0).

¿Cuál es la mejor o la forma más fácil de convertir el std::string a int con la capacidad de fallar? Quiero un C++ versión de C#’s Int32.TryParse.

InformationsquelleAutor | 2009-08-07

5 Comentarios

  1. 42

    Uso boost::lexical_cast. Si el yeso no se puede hacer, va a lanzar una excepción.

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

    Sin impulso:

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

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

    Si quieres no-lanzar versiones de estas funciones, vas a tener que tomar las debidas excepciones (no creo que boost::lexical_cast proporciona una no-lanzar la versión), algo como esto:

    #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;
        }   
    }
  2. 9

    Las otras respuestas que utilizan los flujos de tener éxito incluso si la cadena contiene caracteres no válidos después de un número válido por ejemplo, «123abc». Yo no estoy familiarizado con el impulso, así que no puedo comentar sobre su comportamiento.

    Si quieres saber si la cadena contiene un número y sólo un número, usted tiene que utilizar 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 == '
    #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
    }
    }
    '
    ) { //Success } else { //Failure } }

    strtol devuelve un puntero al carácter que terminó el análisis, así que usted puede comprobar fácilmente si la totalidad de la cadena se ha analizado.

    Nota que strtol devuelve un largo no es un int, pero dependiendo de su compilador estos son probablemente los mismos. No hay strtoi función de la biblioteca estándar, sólo atoi, que no devuelve el analizar el carácter de final.

    • No, he creado una versión que solucione los problemas, a través de secuencias, y de forma genérica.
    • +1 para strtol. Los arroyos son demasiado pesados para la conversión simple.
    • Me gustaría aceptar esto como una especialización de la lexical_cast plantilla, pero de lo contrario prefiero el general de streaming de aplicación.
    • Una alternativa mucho mejor a la corriente de sobrecarga.
  3. 7

    Otra forma, el uso de flujos estándar :

    #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!";
        }
    }
    • Puede crear un bonito general stream_cast el uso de este. Sin embargo, esto tendría un par de especializaciones-es decir, para la transmisión de y en cadenas, como las anteriores, es probable que la pérdida de tiempo de transmisión de la cadena en la corriente, en lugar de la inicialización de la misma.
    • eso se ve tan raro… no estoy seguro de que puedo incluso leer ese código. que pone el número en el number? parece de alguna manera sucia.
    • Esto es en realidad lo que boost::lexical_cast hace internamente.
  4. 7

    Excepciones no deben ser utilizados para booleano pruebas

    La aceptada respuesta es realmente una terrible respuesta para la pregunta, tal como solicitó, ya que se viola el precepto de «usar excepciones para casos excepcionales».

    Excepciones son una excelente herramienta para el manejo de casos excepcionales — casos donde algo realmente ha ido mal. Son pobres las herramientas existentes de casos de uso. En parte debido a lanzar y atrapar una excepción es caro, y en parte porque es engañoso código — cuando un desarrollador ve una excepción que razonablemente debería ser capaz de asumir que algo anda mal ahí. Buenas discusiones de este principio básico abundan, pero me gusta «de La Pragmática Programador»‘s, o esto no es malo: http://www.lohmy.de/2013/03/06/writing-use-cases-exception-or-alternate-flow/

    Usar el boost::lexical_cast si siempre se espera un número

    boost::lexical_cast es una solución óptima cuando se trata de una excepción para recibir un no-número.

    Usar el boost::try_lexical_convert si no los números son parte de su caso de uso

    Si usted va a través de una cadena y quiero hacer una cosa si es un número, y el otro si es un número, no utilice una excepción para el booleano prueba. Que mala programación.

    De hecho, boost ofrece try_lexical_convert, que se utiliza en la aplicación de lexical_cast (tomado de la documentación aquí:
    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;
    }
    • Técnicamente si quiero lanzar una excepción y dejar que el programa correctamente la salida sería una buena idea. La captura de un tipo bool es una idea terrible 100% de acuerdo.
  5. 2

    Antes de boost lexical_cast estaba disponible, yo solía hacer lo siguiente:

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

Dejar respuesta

Please enter your comment!
Please enter your name here