2010-01-28 21 views
7

Tengo un problema con un programa en el que estoy trabajando en C++. Le estoy pidiendo al usuario que ingrese un número válido. Lo tomo como una cadena porque la tarea particular que estoy haciendo, hace que sea más fácil a largo plazo. Para la verificación de errores básicos, quiero verificar si el número ingresado es un número válido. Ejemplo:¿Cómo puedo verificar si un número (tipo doble) almacenado como una cadena es un número doble válido en C++?

Enter number: 3.14 
This would be valid 

Enter number: 3.1456.365.12 
This shouldn't be valid 

Respuesta

7

creo impulso :: lexical_cast debe ayudarle aquí

+0

sí, va a lanzar una excepción 'bad_lexical_cast' si no se puede convertir con éxito. Así que asegúrate de llamarlo siempre en un try/catch. –

+2

boost :: lexical_cast se basa en std :: istringstream por lo que no detectará entradas mal formadas del tipo "3.14.15". – Manuel

+3

@Manuel: en realidad sí. También comprueba que la cadena * completa * se haya convertido correctamente. (Sí, stringstream también lo permite). No estoy seguro, si ese comportamiento es costumizable (uno no necesita ese control siempre) ... – UncleBens

0

Ah, me encantó estas asignaciones. Un buen lexer escrito a mano es el camino a seguir (ya que todavía está en los primeros días - no intente usar boost todavía). Son rápidos, fáciles de escribir y extremadamente divertidos de jugar. Si puede obtener una copia del libro de Levine sobre Lex/Yacc, busque los primeros capítulos para obtener ideas.

+1

También es fácil equivocarse, especialmente si no lo eres. bastante claro en los formatos permitidos. Escribir algunos lexers es divertido, pero déjelos fuera del código de producción. –

+0

Pensé que esto era una tarea como en la tarea. Hm. – dirkgently

3

Puede utilizar strtoX (donde X es f de flotador, l por mucho tiempo, ul de largo sin signo, etc), la elección para el tipo de número que desea. Uno de los parámetros que le da es un "puntero final", que apunta al primer carácter de la cadena que podría no convertirse en el tipo de número de destino.

En su caso, lo que aparentemente está buscando es que el puntero final debe estar al final de la cadena, lo que indica que todos los caracteres de la cadena se convirtieron al tipo de destino.

Editar: Lo siento, no noté que había mencionado 'doble' en el título (pero no la pregunta en sí). Siendo ese el caso, usaría strtod, como también lo han aconsejado otros.

2

La mejor manera es hacer un intento real de convertir la cadena a double utilizando cualquiera de la norma y/o formas idiomáticas que hacer la conversión, y comprobar si hay errores después. En C que serían funciones del grupo strto... (que son, por supuesto, perfectamente utilizables en C++ también). En C++ puede usar expresiones idiomáticas de conversión basadas en secuencias.

Una cosa a tener en cuenta es que la convención común en los métodos de conversión estándar es convertir "tanto como sea posible" y no considerar ningún carácter adicional como un error. Por ejemplo, una cadena "123abc" normalmente se considera entrada válida, con solo la parte "123" convirtiéndose. Todos los métodos utilizables le proporcionan la manera de detectar el hecho de que hay algo extra después del número real, si desea tratar esta situación como un error. Pero depende de usted tomar los pasos adicionales para realizar esta verificación.

9

use strtod, que convierte una cadena en un doble y devuelve los caracteres que no pudo interpretar como parte del doble.

double strtod(const char* nptr, char** endptr) 

De esta manera:

char* input = "3.1456.365.12"; 
char* end; 

strtod(input, &end); 
if (*input == '\0') 
{ 
    printf("fail due to empty string\n"); 
} 
if (end == input || *end != '\0') 
{ 
    printf("fail - the following characters are not part of a double\n%s\n", end); 
} 
+0

ahora probado, funciona. –

+0

También puede fallar debido a subdesbordamiento y desbordamiento que no se detectan con este código. Creo que también deberás probar "inf" y verificar errno para estar seguro. – UncleBens

1

Una opción sencilla es utilizar la función sscanf:

const char * num_as_str = "3.1416"; 
double num; 

if(std::sscanf(num_as_str, "%lg", &num) == 1) 
{ 
    std::cout << "You correctly entered the number " << num << "\n"; 
} 

Si usted desea conseguir la suposición puede utilizar istringstream:

std::istringstream iss(num_as_str); 
if(iss >> num) 
{ 
    std::cout << "You correctly entered the number " << num << "\n"; 
} 

Si desea obtener e Xtra-fantasía puede utilizar lexical_cast de impulso:

try 
{ 
    num = boost::lexical_cast<double>(num_as_str); 
} 
catch(boost::bad_lexical_cast &) 
{ 
    std::cout << "What you entered is not a proper number" << num << "\n"; 
} 
+1

Su ejemplo de cadena de caracteres es incorrecto. Su ejemplo de "3.1456.365.12" informará el éxito. – luke

+1

Ups, eso es cierto. Y las soluciones sscanf y lexical_cast tienen el mismo problema. Parece que la solución correcta es la dada por luke. – Manuel

4

Un ejemplo utilizando sólo estándar de C++:

#include <sstream> 

// ... 

double dbl = 0.0; 
std::istringstream num("3.1456.365.12"); 

num >> dbl; 

if(!num.fail() && 
    num.eof()) // This second test is important! This makes sure that the entire string was converted to a number 
{ 
    // success 
} 
else 
{ 
    // failure 
} 

Bono función de plantilla genérica versión:

#include <sstream> 
#include <string> 
#include <exception> 

// Version that throws an exception on a bad parse: 

template <typename T> 
T parse(const std::string& str) 
{ 
    T value; 
    std::istringstream parser(str); 
    parser >> value; 

    if(!parser.fail() && parser.eof()) 
    { 
     return value; 
    } 
    else 
    { 
     throw "bad lexical cast"; 
    } 
} 

// usage: 

double foo = parse<double>("3.14234"); 

// Non-exception, error code version 

template <typename T> 
bool parse(const std::string& str, T& value) 
{ 
    std::istringstream parser(str); 
    parser >> value; 

    return (!parser.fail() && parser.eof()); 
} 

// usage: 

double foo = 0.0; 
bool success = parser<double>("3.11234", foo); 
+0

no debería ser 'num >> dbl;' en lugar de 'num >> doble;' en su primer ejemplo? .. – smerlin

+0

Sí, buena captura :) – luke

0

Como se ha mencionado por AndreyT, la mejor es intentar convertir la cadena en un flotador y verificar si hay un error. Personalmente, optaría por usar std :: istringstream, ya que estás usando C++. Algo parecido a lo siguiente debería funcionar:

float ff; 
std::istringstream istr; 
std::string input("1234.5678"); 

// set the stream to have your string as its base 
istr.str(input); 

// now try to read the number: 
istr >> ff; 
if (istr.fail()) 
{ 
    // some error in the parsing 
} 

istringstream es parte de STL, por lo que no es necesario ningún bibliotecas adicionales, y también con las excepciones si esa es su elección. Puede encontrar más información aquí: http://www.cplusplus.com/reference/iostream/istringstream/

+0

Al igual que la respuesta de Manuel, su ejemplo informará incorrectamente el éxito en el ejemplo de John "3.1456.365.12 " – luke

+0

Sí, parece que este es un buen ejemplo de C++ gotcha :) – Manuel

+0

Sí :) Vale la pena probar – luke

0

Puede usar expresiones regulares. Puesto que usted ya tiene una cadena, sería fácil comparar que con esta expresión regular:

/^\d+(\.\d+)?$/ 

El regex.h biblioteca puede ayudar aquí. Ver esto: regex.h

+0

¿Qué pasa con la notación científica? Comprobar que en realidad se ajusta al rango del doble? (Cuando tienes un problema, se sugiere regex. Ahora tienes dos problemas . :)) – UncleBens

+0

¿Quién dijo algo acerca de verificar el rango del doble o la notación científica? ;-) Los RegExes son geniales, pero admito que es difícil verificar que sean correctos. – Aaron

0

Ésta es mi truco rápido :)

#include <iostream> 
#include <string> 
#include <sstream> 
using namespace std; 

template <typename T> 
bool fromStr(const std::string& str, T& var) 
{ 
    std::stringstream convertor; 
    convertor << str; 
    convertor >> var; 

    if(convertor.fail()) 
     return false; 

    char c = static_cast<char>(convertor.get()); 

    return convertor.eof() || c == '\n' || c == ' ' || c == '\t'; 
} 

int main() 
{ 
    double d; 
    std::string str = "5.04146.55"; 

    if(fromStr<double>(str, d)) 
    { 
     std::cout << "Valid conversion!, d = " << d; 
    } 
    else 
    { 
     std::cout << "Invalid conversion!"; 
    } 
} 
Cuestiones relacionadas