validar - ¿Cuál es la mejor manera de hacer la validación de entrada en C++ con cin?
validar solo numeros en matlab (8)
¿Qué tal una combinación de los diferentes enfoques?
Toma la entrada de
std::cin
usandostd::getline(std::cin, strObj)
dondestrObj
es un objetostd::string
.Utilice
boost::lexical_cast
para realizar una traducción léxica destrObj
a un entero con signo o sin signo de mayor ancho (por ejemplo,unsigned long long
o algo similar)Utilice
boost::numeric_cast
para convertir el entero al rango esperado.
Simplemente puede obtener la entrada con std::getline
y luego invocar boost::lexical_cast
al tipo entero estrecho adecuado, dependiendo de dónde desee detectar el error. El enfoque de tres pasos tiene el beneficio de aceptar cualquier dato entero y luego captura los errores de estrechamiento por separado.
Mi hermano recientemente comenzó a aprender C ++. Él me contó un problema que encontró al intentar validar la entrada en un programa simple. Tenía un menú de texto donde el usuario ingresaba una choice
entera, si ingresaban una opción no válida, se les pediría que la ingresaran nuevamente (hacer while loop). Sin embargo, si el usuario ingresara una cadena en lugar de una int, el código se rompería. Leí varias preguntas sobre stackoverflow y le dije que reescribiera su código de la siguiente manera:
#include<iostream>
using namespace std;
int main()
{
int a;
do
{
cout<<"/nEnter a number:"
cin>>a;
if(cin.fail())
{
//Clear the fail state.
cin.clear();
//Ignore the rest of the wrong user input, till the end of the line.
cin.ignore(std::numeric_limits<std::streamsize>::max(),/
''/n'');
}
}while(true);
return 0;
}
Si bien esto funcionó bien, también probé algunas otras ideas:
1. Usando un bloque try catch. No funcionó. Creo que esto se debe a que una excepción no se plantea debido a una mala entrada. 2. Intenté if(! cin){//Do Something}
que tampoco funcionaba. Todavía no he descubierto esto.
3. En tercer lugar, traté de ingresar una cadena de longitud fija y luego analizarla. Yo usaría atoi (). ¿Es compatible y compatible con los estándares? ¿Debo escribir mi propia función de análisis?
4. Si escribe una clase que usa cin, pero realiza este tipo de detección de error dinámicamente, tal vez determinando el tipo de la variable de entrada en tiempo de ejecución, ¿tendría demasiada sobrecarga? ¿Es posible?
Me gustaría saber cuál es la mejor manera de hacer este tipo de comprobación, ¿cuáles son las mejores prácticas?
Me gustaría añadir que, aunque no soy nuevo en la escritura de código C ++, soy nuevo en la redacción de código compatible con los estándares. Estoy tratando de desaprender las malas prácticas y aprender las correctas. Me agradaría mucho que los contestadores brinden una explicación detallada.
EDITAR : veo que litb ha respondido a uno de mis ediciones anteriores. Voy a publicar ese código aquí para referencia.
#include<iostream>
using namespace std;
int main()
{
int a;
bool inputCompletionFlag = true;
do
{
cout<<"/nEnter a number:"
cin>>a;
if(cin.fail())
{
//Clear the fail state.
cin.clear();
//Ignore the rest of the wrong user input, till the end of the line.
cin.ignore(std::numeric_limits<std::streamsize>::max(),/
''/n'');
}
else
{
inputCompletionFlag = false;
}
}while(!inputCompletionFlag);
return 0;
}
Este código falla en la entrada como "1asdsdf". No sabía cómo solucionarlo, pero litb ha publicado una gran respuesta. :)
Esto es lo que hago con C, pero probablemente también sea aplicable para C ++.
Ingrese todo como una cadena.
Luego, y solo entonces, analiza la cadena en lo que necesitas. A veces es mejor codificar el tuyo que intentar doblegar el de alguien más a tu voluntad.
Estoy de acuerdo con Pax, la forma más sencilla de hacer esto es leer todo como una cadena, luego use TryParse para verificar la entrada. Si está en el formato correcto, proceda, otra cosa simplemente notifique al usuario y use continue en el ciclo.
Lo que haría sería doble: Primero, intente validar la entrada y extraiga los datos, usando una expresión regular, si la entrada no es algo trivial. Puede ser muy útil incluso si la entrada es solo una serie de números.
Luego, me gusta usar boost :: lexical_cast , que puede generar una excepción bad_ lexical_ cast si la entrada no se puede convertir.
En tu ejemplo:
std::string in_str;
cin >> in_str;
// optionally, test if it conforms to a regular expression, in case the input is complex
// Convert to int? this will throw bad_lexical_cast if cannot be converted.
int my_int = boost::lexical_cast<int>(in_str);
Olvídate de usar entrada formateada (el operador >>) directamente en código real. Siempre necesitará leer texto sin formato con std :: getline o similar y luego usar sus propias rutinas de análisis de entrada (que pueden usar el operador >>) para analizar la entrada.
Una cosa que aún no se ha mencionado es que generalmente es importante que pruebes para ver si la operación cin >> funcionó antes de usar la variable que supuestamente obtuvo algo de la transmisión.
Este ejemplo es similar al tuyo, pero hace esa prueba.
#include <iostream>
#include <limits>
using namespace std;
int main()
{
while (true)
{
cout << "Enter a number: " << flush;
int n;
if (cin >> n)
{
// do something with n
cout << "Got " << n << endl;
}
else
{
cout << "Error! Ignoring..." << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), ''/n'');
}
}
return 0;
}
Esto usará la >> semántica del operador habitual; saltará primero el espacio en blanco, luego intentará leer tantos dígitos como sea posible y luego se detendrá. Así que "42crap" te dará los 42 y saltaras la "mierda". Si eso no es lo que quieres, entonces estoy de acuerdo con las respuestas anteriores, debes leerlo en una cadena y luego validarlo (quizás usando una expresión regular, pero puede ser exagerado para una secuencia numérica simple).
- Para obtener las excepciones con iostreams , debe establecer el indicador de excepción adecuado para la transmisión.
- Y usaría get_line para obtener toda la línea de entrada y luego manejarla en consecuencia - use lexical_cast, expresiones regulares (por ejemplo, Boost Regex o Boost Xpressive , analízalo con Boost Spirit , o simplemente usa algún tipo de lógica apropiada
Aquí hay un código que puedes usar para asegurarte de que también rechazas cosas como
42crap
Donde los caracteres no numéricos siguen el número. Si lees toda la línea y luego lo analiza y ejecuta las acciones de manera apropiada, es posible que necesites cambiar la forma en que funciona tu programa. Si su programa leyó su número desde diferentes lugares hasta ahora, entonces tiene que poner un lugar central que analiza una línea de entrada, y decide sobre la acción. Pero tal vez eso también sea bueno, por lo que podría aumentar la legibilidad del código de esa manera al separar las cosas: I nput - P rocessing - O utput
De todos modos, así es como puedes rechazar el número-no-número de arriba. Lea una línea en una cadena, luego stringstream
con un stringstream
:
std::string getline() {
std::string str;
std::getline(std::cin, str);
return str;
}
int choice;
std::istringstream iss(getline());
iss >> choice >> std::ws;
if(iss.fail() || !iss.eof()) {
// handle failure
}
Come todos los espacios en blanco al final. Cuando llega al final del archivo del stringstream mientras lee el entero o el espacio en blanco al final, entonces establece el eof-bit, y lo comprobamos. Si no pudo leer ningún número entero en primer lugar, entonces se habrá establecido el bit fallido o malo.
Las versiones anteriores de esta respuesta usaban std::cin
directamente, pero std::ws
no funcionaría bien junto con std::cin
conectado a un terminal (bloqueará en lugar de esperar que el usuario ingrese algo), entonces usaremos un stringstream
para leer el entero.
Respondiendo algunas de sus preguntas:
Pregunta: 1. Usando un bloque try catch. No funcionó. Creo que esto se debe a que una excepción no se plantea debido a una mala entrada.
Respuesta: Bueno, puedes decirle a la transmisión que haga excepciones cuando leas algo. Utiliza la función istream::exceptions
, que indica qué tipo de error desea que se genere una excepción:
iss.exceptions(ios_base::failbit);
Nunca lo usé Si lo haces en std::cin
, deberás recordar restaurar las banderas para que otros lectores confíen en que no se lanzarán. Es mucho más fácil simplemente usar las funciones, es malo preguntar por el estado de la transmisión.
Pregunta: 2. Intenté if(!cin){ //Do Something }
que tampoco funcionaba. Todavía no he descubierto esto.
Respuesta: Eso podría provenir del hecho de que le diste algo como "42crap". Para la transmisión, es una entrada completamente válida cuando se realiza una extracción en un número entero.
Pregunta: 3. En tercer lugar, traté de ingresar una cadena de longitud fija y luego analizarla. Yo usaría atoi (). ¿Es compatible y compatible con los estándares? ¿Debo escribir mi propia función de análisis?
Respuesta: atoi cumple con los estándares. Pero no es bueno cuando quieres verificar si hay errores. No hay verificación de errores, hecho por él en comparación con otras funciones. Si tiene una cadena y desea verificar si contiene un número, hágalo como en el código inicial anterior.
Hay funciones similares a C que pueden leer directamente desde una C-cadena. Existen para permitir la interacción con el código heredado antiguo y escribir código de ejecución rápida. Uno debe evitarlos en los programas porque trabajan bastante bajo nivel y requieren el uso de punteros desnudos. Por su propia naturaleza, tampoco se pueden mejorar para que funcionen con tipos definidos por el usuario. Específicamente, esto se refiere a la función "strtol" (de cadena a longitud) que básicamente es atoi con comprobación de errores y capacidad para trabajar con otras bases (por ejemplo, hexadecimal).
Pregunta: 4. Si escribo una clase que usa cin, pero dinámicamente hago este tipo de detección de errores, quizás al determinar el tipo de la variable de entrada en tiempo de ejecución, ¿tendrá demasiada sobrecarga? ¿Es posible?
Respuesta: En general, no es necesario que se preocupe demasiado por los gastos generales aquí (si se refiere a tiempo de ejecución-overhead). Pero depende específicamente de dónde use esa clase. Esa pregunta será muy importante si está escribiendo un sistema de alto rendimiento que procesa las aportaciones y necesita tener alto durante todo el proceso. Pero si necesita leer la entrada desde un terminal o un archivo, ya verá a qué se reduce esto: esperar que el usuario ingrese algo tarda tanto, no es necesario que mire los costos de tiempo de ejecución en este punto en este momento. escala.
Si se refiere a la sobrecarga del código, depende de cómo se implemente el código. Debería escanear la cadena que ha leído, ya sea que contenga un número o no, si se trata de una cadena arbitraria. Dependiendo de lo que desea escanear (tal vez tiene una entrada de "fecha", o un formato de entrada de "tiempo" también. Mire en boost.date_time
para eso), su código puede volverse arbitrariamente complejo. Para cosas simples como clasificar entre números o no, creo que se puede salir con poca cantidad de código.