tipos - ¿Por qué scanf() causa un bucle infinito en este código?
tipos de bucles en programacion (13)
Tengo un pequeño programa C que solo lee números de stdin, uno en cada ciclo de ciclo. Si el usuario ingresa algo de NaN, se debe imprimir un error en la consola y el mensaje de entrada debe regresar nuevamente. En la entrada de "0", el ciclo debe finalizar y la cantidad de valores positivos / negativos dados debe imprimirse en la consola. Aquí está el programa:
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
printf("Err.../n");
continue;
}
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers/n", p, n);
return 0;
}
Mi problema es que al ingresar un número no (como "a"), esto da como resultado un ciclo de escritura infinito "-> Err ..." una y otra vez. Supongo que es un problema de escaneo (scanf) y sé que esta función podría reemplazarse por una más segura, pero este ejemplo es para principiantes, ya que saben de printf / scanf, if-else y loops.
Ya leí las respuestas a esta pregunta y hojeé otras preguntas, pero nada realmente responde a este problema específico.
Buenas tardes. Recientemente he tenido el mismo problema y encontré una solución que podría ayudar a muchos hombres. Bueno, en realidad la función "scanf" deja un búfer en la memoria ... y es por eso que el bucle infinito es causado. Entonces, realmente tiene que "almacenar" este buffer en otra variable SI su scanf inicial contiene el valor "nulo". Esto es lo que quiero decir:
#include <stdio.h>
int n;
char c[5];
main() {
while (1) {
printf("Input Number: ");
if (scanf("%d", &n)==0) { //if you type char scanf gets null value
scanf("%s", &c); //the abovementioned char stored in ''c''
printf("That wasn''t a number: %s/n", c);
}
else printf("The number is: %d/n", n);
}
}
Creo que solo tienes que lavar el búfer antes de continuar con el bucle. Algo así probablemente haría el trabajo, aunque no puedo probar lo que estoy escribiendo desde aquí:
int c;
while((c = getchar()) != ''/n'' && c != EOF);
Cuando se ingresa un no número, se produce un error y el no número se mantiene en el búfer de entrada. Deberías omitirlo. También incluso esta combinación de símbolos como por ejemplo 1a
se leerá al principio como número 1, creo que también debería omitir dicha entrada.
El programa puede verse de la siguiente manera.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int p = 0, n = 0;
while (1)
{
char c;
int number;
int success;
printf("-> ");
success = scanf("%d%c", &number, &c);
if ( success != EOF )
{
success = success == 2 && isspace( ( unsigned char )c );
}
if ( ( success == EOF ) || ( success && number == 0 ) ) break;
if ( !success )
{
scanf("%*[^ /t/n]");
clearerr(stdin);
}
else if ( number > 0 )
{
++p;
}
else if ( number < n )
{
++n;
}
}
printf( "/nRead %d positive and %d negative numbers/n", p, n );
return 0;
}
El resultado del programa podría verse como
-> 1
-> -1
-> 2
-> -2
-> 0a
-> -0a
-> a0
-> -a0
-> 3
-> -3
-> 0
Read 3 positive and 3 negative numbers
Debido a los problemas con scanf
señalados por las otras respuestas, realmente debería considerar usar otro enfoque. Siempre he encontrado que scanf
demasiado limitado para cualquier lectura y procesamiento de entrada serio. Es una mejor idea leer líneas enteras con fgets
y luego trabajar en ellas con funciones como strtok
y strtol
(que, por cierto, analizarán correctamente los enteros y te dirán exactamente dónde comienzan los caracteres no válidos).
En algunas plataformas (especialmente Windows y Linux) puede usar fflush(stdin);
:
#include <stdio.h>
int main(void)
{
int number, p = 0, n = 0;
while (1) {
printf("-> ");
if (scanf("%d", &number) == 0) {
fflush(stdin);
printf("Err.../n");
continue;
}
fflush(stdin);
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
printf("Read %d positive and %d negative numbers/n", p, n);
return 0;
}
En lugar de usar scanf()
y tener que lidiar con el buffer que tiene un carácter no válido, use fgets()
y sscanf()
.
/* ... */
printf("0 to quit -> ");
fflush(stdout);
while (fgets(buf, sizeof buf, stdin)) {
if (sscanf(buf, "%d", &number) != 1) {
fprintf(stderr, "Err.../n");
} else {
work(number);
}
printf("0 to quit -> ");
fflush(stdout);
}
/* ... */
Hola, sé que este es un hilo viejo, pero acabo de terminar una tarea escolar y me encontré con este mismo problema. Mi solución es que utilicé get () para recoger lo que scanf () dejó atrás.
Aquí está el código OP ligeramente reescrito; probablemente no sea útil para él, pero quizás ayude a alguien más allá.
#include <stdio.h>
int main()
{
int number, p = 0, n = 0;
char unwantedCharacters[40]; //created array to catch unwanted input
unwantedCharacters[0] = 0; //initialzed first byte of array to zero
while (1)
{
printf("-> ");
scanf("%d", &number);
gets(unwantedCharacters); //collect what scanf() wouldn''t from the input stream
if (unwantedCharacters[0] == 0) //if unwantedCharacters array is empty (the user''s input is valid)
{
if (number > 0) p++;
else if (number < 0) n++;
else break; /* 0 given */
}
else
printf("Err.../n");
}
printf("Read %d positive and %d negative numbers/n", p, n);
return 0;
}
Intenta usar esto:
if (scanf("%d", &number) == 0) {
printf("Err.../n");
break;
}
esto funcionó bien para mí ... prueba esto ... la declaración de continuar no es apropiada ya que el Err .. solo debe ejecutar una vez. así que prueba el descanso que probé ... esto funcionó bien para ti ... probé ...
Tuve el mismo problem y encontré una solución algo hacky. Yo uso fgets()
para leer la entrada y luego alimentar a sscanf()
. Esta no es una mala solución para el problema de bucle infinito, y con un bucle for simple, le digo a C que busque cualquier carácter no numérico. El código siguiente no permitirá entradas como 123abc
.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char line[10];
int loop, arrayLength, number, nan;
arrayLength = sizeof(line) / sizeof(char);
do {
nan = 0;
printf("Please enter a number:/n");
fgets(line, arrayLength, stdin);
for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array
if(line[loop] == ''/n'') { // stop the search if there is a carrage return
break;
}
if((line[0] == ''-'' || line[0] == ''+'') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers
continue;
}
if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop
nan++;
break;
}
}
} while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter
sscanf(line, "%d", &number);
printf("You enterd number %d/n", number);
return 0;
}
Tuve un problema similar. Lo resolví usando solo scanf.
Input "abc123<Enter>"
para ver cómo funciona.
#include <stdio.h>
int n, num_ok;
char c;
main() {
while (1) {
printf("Input Number: ");
num_ok = scanf("%d", &n);
if (num_ok != 1) {
scanf("%c", &c);
printf("That wasn''t a number: %c/n", c);
} else {
printf("The number is: %d/n", n);
}
}
}
Vacíe el búfer de entrada antes de escanear:
while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
...
Iba a sugerir fflush(stdin)
, pero aparentemente eso da como resultado un comportamiento indefinido .
En respuesta a su comentario, si desea que se muestre el aviso, debe vaciar el búfer de salida. Por defecto, eso solo ocurre cuando imprime una nueva línea. Me gusta:
while (1) {
printf("-> ");
fflush(stdout);
while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
...
scanf()
deja la " a
" todavía en el búfer de entrada para la próxima vez. Probablemente deberías usar getline()
para leer una línea sin importar qué y luego analizarla con strtol()
o similar en su lugar.
(Sí, getline()
es específico de GNU, no POSIX. ¿Y qué? La pregunta está etiquetada como "gcc" y "linux". getline()
también es la única opción sensata para leer una línea de texto a menos que quieras hacerlo todo a mano.)
scanf
solo consume la entrada que coincide con la cadena de formato, devolviendo la cantidad de caracteres consumidos. Cualquier carácter que no coincida con la cadena de formato hace que deje de escanear y deja el carácter no válido aún en el búfer. Como dijeron otros, aún necesita eliminar el carácter inválido del búfer antes de continuar. Esta es una solución bastante sucia, pero eliminará los caracteres ofensivos de la salida.
char c = ''0'';
if (scanf("%d", &number) == 0) {
printf("Err. . ./n");
do {
c = getchar();
}
while (!isdigit(c));
ungetc(c, stdin);
//consume non-numeric chars from buffer
}
editar: corrigió el código para eliminar todos los caracteres no numéricos de una sola vez. Ya no imprimirá múltiples "errores" para cada carácter no numérico.
Here hay una muy buena descripción de scanf.