usando - uso apropiado de scanf en un ciclo while para validar entrada
validar numeros en c (4)
Hice este código:
/*here is the main function*/
int x , y=0, returned_value;
int *p = &x;
while (y<5){
printf("Please Insert X value/n");
returned_value = scanf ("%d" , p);
validate_input(returned_value, p);
y++;
}
la función:
void validate_input(int returned_value, int *p){
getchar();
while (returned_value!=1){
printf("invalid input, Insert Integers Only/n");
getchar();
returned_value = scanf("%d", p);
}
}
Aunque generalmente funciona muy bien, pero cuando inserto, por ejemplo, "1f1", acepta el "1" y no informa ningún error, y cuando inserta "f1f1f", lo lee dos veces y arruina la segunda lectura / exploración, y así sucesivamente ( es decir, la primera lectura imprime "entrada no válida, inserte enteros solamente" y en lugar de esperar nuevamente para volver a leer la primera lectura del usuario, continúa hasta la segunda lectura y vuelve a imprimir "entrada no válida, inserte enteros solamente" de nuevo ...
Necesita un toque final y leí muchas respuestas pero no pude encontrarlo.
Si está leyendo un flujo de entrada que sabe que es una secuencia de texto, pero que no está seguro solo consiste en enteros, entonces lea cadenas.
Además, una vez que haya leído una cadena y desee ver si se trata de un número entero, use la rutina de conversión de biblioteca estándar strtol()
. Al hacer esto, ambos obtienen una confirmación de que se trata de un número entero y lo convierten en un long
.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool convert_to_long(long *number, const char *string)
{
char *endptr;
*number = strtol(string, &endptr, 10);
/* endptr will point to the first position in the string that could
* not be converted. If this position holds the string terminator
* ''/0'' the conversion went well. An empty input string will also
* result in *endptr == ''/0'', so we have to check this too, and fail
* if this happens.
*/
if (string[0] != ''/0'' && *endptr == ''/0'')
return false; /* conversion succesful */
return true; /* problem in conversion */
}
int main(void)
{
char buffer[256];
const int max_tries = 5;
int tries = 0;
long number;
while (tries++ < max_tries) {
puts("Enter input:");
scanf("%s", buffer);
if (!convert_to_long(&number, buffer))
break; /* returns false on success */
printf("Invalid input. ''%s'' is not integer, %d tries left/n", buffer,
max_tries - tries);
}
if (tries > max_tries)
puts("No valid input found");
else
printf("Valid input: %ld/n", number);
return EXIT_SUCCESS;
}
AÑADIDO NOTA : Si cambia la base
(el último parámetro a strtol()
) de 10 a cero, obtendrá la característica adicional de que su código convierte números hexadecimales y octales (cadenas que comienzan con 0x
y 00
respectivamente) en enteros.
Si no quiere aceptar 1f1
como entrada válida, entonces scanf
es la función incorrecta para usar, ya que scanf
regresa tan pronto como encuentra una coincidencia.
En su lugar, lea toda la línea y luego verifique si solo contiene dígitos. Después de eso puedes llamar scanf
Algo como:
#include <stdio.h>
int validateLine(char* line)
{
int ret=0;
// Allow negative numbers
if (*line && *line == ''-'') line++;
// Check that remaining chars are digits
while (*line && *line != ''/n'')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1; // Remember that at least one legal digit was found
++line;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value/n");
if (fgets(line, sizeof(line), stdin)) // Read the whole line
{
if (validateLine(line)) // Check that the line is a valid number
{
// Now it should be safe to call scanf - it shouldn''t fail
// but check the return value in any case
if (1 != sscanf(line, "%d", &x))
{
printf("should never happen");
exit(1);
}
// Legal number found - break out of the "while (y<5)" loop
break;
}
else
{
printf("Illegal input %s", line);
}
}
y++;
}
if (y<5)
printf("x=%d/n", x);
else
printf("no more retries/n");
return 0;
}
Entrada
1f1
f1f1
-3
Salida
Please Insert X value
Illegal input 1f1
Please Insert X value
Illegal input f1f1
Please Insert X value
Illegal input
Please Insert X value
x=-3
Otro enfoque: evitar scanf
Puede dejar que su función calcule el número y omitir scanf por completo. Podría verse así:
#include <stdio.h>
int line2Int(char* line, int* x)
{
int negative = 0;
int ret=0;
int temp = 0;
if (*line && *line == ''-'')
{
line++;
negative = 1;
}
else if (*line && *line == ''+'') // If a + is to be accepted
line++; // If a + is to be accepted
while (*line && *line != ''/n'')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1;
// Update the number
temp = 10 * temp;
temp = temp + (*line - ''0'');
++line;
}
if (ret)
{
if (negative) temp = -temp;
*x = temp;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value/n");
if (fgets(line, sizeof(line), stdin))
{
if (line2Int(line, &x)) break; // Legal number - break out
printf("Illegal input %s", line);
}
y++;
}
if (y<5)
printf("x=%d/n", x);
else
printf("no more retries/n");
return 0;
}
Tomé @ 4386427 idea y acabo de agregar códigos para cubrir lo que se perdió (espacios iniciales y signo +), lo probé muchas veces y está funcionando perfectamente en todos los casos posibles.
#include<stdio.h>
#include <ctype.h>
#include <stdlib.h>
int validate_line (char *line);
int main(){
char line[256];
int y=0;
long x;
while (y<5){
printf("Please Insert X Value/n");
if (fgets(line, sizeof(line), stdin)){//return 0 if not execute
if (validate_line(line)>0){ // check if the string contains only numbers
x =strtol(line, NULL, 10); // change the authentic string to long and assign it
printf("This is x %d" , x);
break;
}
else if (validate_line(line)==-1){printf("You Have Not Inserted Any Number!.... ");}
else {printf("Invalid Input, Insert Integers Only.... ");}
}
y++;
if (y==5){printf("NO MORE RETRIES/n/n");}
else{printf("%d Retries Left/n/n", (5-y));}
}
return 0;}
int validate_line (char *line){
int returned_value =-1;
/*first remove spaces from the entire string*/
char *p_new = line;
char *p_old = line;
while (*p_old != ''/0''){// loop as long as has not reached the end of string
*p_new = *p_old; // assign the current value the *line is pointing at to p
if (*p_new != '' ''){p_new++;} // check if it is not a space , if so , increment p
p_old++;// increment p_old in every loop
}
*p_new = ''/0''; // add terminator
if (*line== ''+'' || *line== ''-''){line++;} // check if the first char is (-) or (+) sign to point to next place
while (*line != ''/n''){
if (!(isdigit(*line))) {return 0;} // Illegal char found , will return 0 and stop because isdigit() returns 0 if the it finds non-digit
else if (isdigit(*line)){line++; returned_value=2;}//check next place and increment returned_value for the final result and judgment next.
}
return returned_value; // it will return -1 if there is no input at all because while loop has not executed, will return >0 if successful, 0 if invalid input
}
En general, opino que es mejor leer todo desde la entrada (dentro del rango de su tamaño de búfer, por supuesto), y luego validar la entrada es de hecho el formato correcto.
En su caso, está viendo errores usando una cadena como f1f1f
porque no está leyendo todo el buffer STDIN. Como tal, cuando va a volver a llamar a scanf(...)
, todavía hay datos dentro de STDIN, por lo que se lee primero en lugar de pedir al usuario que ingrese más entradas. Para leer todo STDIN, debe hacer algo de lo siguiente (parte del código tomado de la respuesta de Paxdiablo aquí: https://.com/a/4023921/2694511 ):
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // Used for strtol
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define NaN 3 // Not a Number (NaN)
int strIsInt(const char *ptrStr){
// Check if the string starts with a positive or negative sign
if(*ptrStr == ''+'' || *ptrStr == ''-''){
// First character is a sign. Advance pointer position
ptrStr++;
}
// Now make sure the string (or the character after a positive/negative sign) is not null
if(*ptrStr == NULL){
return NaN;
}
while(*ptrStr != NULL){
// Check if the current character is a digit
// isdigit() returns zero for non-digit characters
if(isdigit( *ptrStr ) == 0){
// Not a digit
return NaN;
} // else, we''ll increment the pointer and check the next character
ptrStr++;
}
// If we have made it this far, then we know that every character inside of the string is indeed a digit
// As such, we can go ahead and return a success response here
// (A success response, in this case, is any value other than NaN)
return 0;
}
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there''ll be no newline. In that case, we flush
// to end of line so that excess doesn''t affect the next call.
// (Per Chux suggestions in the comments, the "buff[0]" condition
// has been added here.)
if (buff[0] && buff[strlen(buff)-1] != ''/n'') {
extra = 0;
while (((ch = getchar()) != ''/n'') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = ''/0'';
return OK;
}
void validate_input(int responseCode, char *prompt, char *buffer, size_t bufferSize){
while( responseCode != OK ||
strIsInt( buffer ) == NaN )
{
printf("Invalid input./nPlease enter integers only!/n");
fflush(stdout); /* It might be unnecessary to flush here because we''ll flush STDOUT in the
getLine function anyway, but it is good practice to flush STDOUT when printing
important information. */
responseCode = getLine(prompt, buffer, bufferSize); // Read entire STDIN
}
// Finally, we know that the input is an integer
}
int main(int argc, char **argv){
char *prompt = "Please Insert X value/n";
int iResponseCode;
char cInputBuffer[100];
int x, y=0;
int *p = &x;
while(y < 5){
iResponseCode = getLine(prompt, cInputBuffer, sizeof(cInputBuffer)); // Read entire STDIN buffer
validate_input(iResponseCode, prompt, cInputBuffer, sizeof(cInputBuffer));
// Once validate_input finishes running, we should have a proper integer in our input buffer!
// Now we''ll just convert it from a string to an integer, and store it in the P variable, as you
// were doing in your question.
sscanf(cInputBuffer, "%d", p);
y++;
}
}
Solo como un descargo de responsabilidad / nota: hace mucho tiempo que no escribo en C, así que me disculpo por adelantado si hay algún error en este ejemplo. Tampoco tuve la oportunidad de compilar y probar este código antes de publicarlo porque estoy apurado en este momento.