¿Cómo escanear solo un entero?
scanf (7)
Quiero que el código se ejecute hasta que el usuario ingrese un valor entero.
El código funciona para matrices char y char.
He hecho lo siguiente:
#include<stdio.h>
int main()
{
int n;
printf("Please enter an integer: ");
while(scanf("%d",&n) != 1)
{
printf("Please enter an integer: ");
while(getchar() != ''/n'');
}
printf("You entered: %d/n",n);
return 0;
}
El problema es si el usuario ingresa un valor flotante
scanf
lo aceptará.
Please enter an integer: abcd
Please enter an integer: a
Please enter an integer: 5.9
You entered: 5
¿Cómo se puede evitar eso?
Sé cómo se puede hacer esto usando
fgetsystrtol, me gustaría saber cómo se puede hacer esto usandoscanf()(si es posible).
Como dicen las otras respuestas,
scanf
no es realmente adecuado para esto,
fgets
y
strtol
son una alternativa (aunque
fgets
tiene el inconveniente de que es difícil detectar un byte 0 en la entrada e imposible saber qué se ha ingresado después de un 0 -byte, si lo hay).
En aras de la integridad (y suponiendo que la entrada válida es un número entero seguido de una nueva línea):
while(scanf("%d%1[/n]", &n, (char [2]){ 0 }) < 2)
Alternativamente, use
%n
antes y después de
%*1[/n]
con supresión de asignación.
Sin embargo, tenga en cuenta (de la página de
manual de Debian
):
Esto no es una conversión, aunque puede suprimirse con el carácter
*asignación-supresión. El estándar C dice: "La ejecución de una directiva%nno incrementa el recuento de asignaciones devuelto al finalizar la ejecución", pero el Corrigendum parece contradecir esto. Probablemente es aconsejable no hacer suposiciones sobre el efecto de%nconversiones en el valor de retorno.
-
Tomas
scanf(). - Lo tiras a la basura.
-
Utiliza
fgets()para obtener una línea completa. -
Utiliza
strtol()para analizar la línea como un entero, comprobando si consumió toda la línea.
char *end;
char buf[LINE_MAX];
do {
if (!fgets(buf, sizeof buf, stdin))
break;
// remove /n
buf[strlen(buf) - 1] = 0;
int n = strtol(buf, &end, 10);
} while (end != buf + strlen(buf));
Intente usar el siguiente patrón en
scanf
.
Leerá hasta el final de la línea:
scanf("%d/n", &n)
No necesitará
getchar()
dentro del bucle ya que
scanf
leerá toda la línea.
Los flotantes no coincidirán con el patrón
scanf
y la solicitud le pedirá un número entero nuevamente.
Si está configurado para usar
scanf
, puede hacer algo como lo siguiente:
int val;
char follow;
int read = scanf( "%d%c", &val, &follow );
if ( read == 2 )
{
if ( isspace( follow ) )
{
// input is an integer followed by whitespace, accept
}
else
{
// input is an integer followed by non-whitespace, reject
}
}
else if ( read == 1 )
{
// input is an integer followed by EOF, accept
}
else
{
// input is not an integer, reject
}
Una posible solución es pensarlo al revés: aceptar un flotante como entrada y rechazar la entrada si el flotante no es un entero:
int n;
float f;
printf("Please enter an integer: ");
while(scanf("%f",&f)!=1 || (int)f != f)
{
...
}
n = f;
Aunque esto permite al usuario ingresar algo como 12.0, o 12e0, etc.
Usa
fgets
y
strtol
,
Un puntero al primer carácter que sigue a la representación entera en
s
se almacena en el objeto señalado por
p
, si
*p
es diferente de
/n
entonces tiene una entrada incorrecta.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *p, s[100];
long n;
while (fgets(s, sizeof(s), stdin)) {
n = strtol(s, &p, 10);
if (p == s || *p != ''/n'') {
printf("Please enter an integer: ");
} else break;
}
printf("You entered: %ld/n", n);
return 0;
}
Usar
fgets()
es mejor.
Para resolver solo usando
scanf()
para la entrada, busque un
int
y el siguiente
char
.
int ReadUntilEOL(void) {
char ch;
int count;
while ((count = scanf("%c", &ch)) == 1 && ch != ''/n'')
; // Consume char until /n or EOF or IO error
return count;
}
#include<stdio.h>
int main(void) {
int n;
for (;;) {
printf("Please enter an integer: ");
char NextChar = ''/n'';
int count = scanf("%d%c", &n, &NextChar);
if (count >= 1 && NextChar == ''/n'')
break;
if (ReadUntilEOL() == EOF)
return 1; // No valid input ever found
}
printf("You entered: %d/n", n);
return 0;
}
Este enfoque no vuelve a solicitar si el usuario solo ingresa espacios en blanco, como solo Enter .