¿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
fgets
ystrtol
, 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%n
no 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%n
conversiones 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 .