2010-09-16 9 views
6

Si ingreso 5 5 en el terminal, presione enter, y presione enter nuevamente, quiero salir del ciclo.¿Obtenga scanf para salir cuando lee una nueva línea?

int readCoefficents(double complex *c){ 
    int i = 0; 
    double real; 
    double img; 
    while(scanf("%f %f", &real, &img) == 2) 
     c[i++] = real + img * I; 


    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
} 

Obviamente, ese código no está haciendo el trabajo para mí (y sí, sé que hay un desbordamiento de búfer a punto de ocurrir).

scanf no saldrá a menos que escriba una letra (o alguna cadena no numérica, no espacios en blanco). ¿Cómo obtengo scanf para cerrar después de leer una línea vacía?

+0

En caso de que necesite más razones para evitar el uso de 'scanf': http://c-faq.com/stdio/scanfprobs.html – jamesdlin

Respuesta

7

Use fgets para leer la consola de entrada:

int res = 2; 
    while (res == 2) { 
     char buf[100]; 
     fgets(buf, sizeof(buf), stdin); 
     res = sscanf(buf, "%f %f", &real, &img); 
     if (res == 2) 
      c[i++] = real + img * I; 
    } 
    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
+0

Le da el crédito dado que usted mencionó primero los fgets y sscanf. – Matt

+0

Gracias, acabo de dar cuenta de que c no le permitirá declarar el búfer dentro del ciclo while, pero el concepto está ahí. – MattSmith

+2

Esa declaración está perfectamente bien. – jamesdlin

9

El problema específico que está teniendo es que una cadena scanf formato de %f saltará espacio en blanco (incluyendo saltos de línea) hasta que encuentra un carácter real a escanear. Desde el estándar c99:

Una especificación de conversión se ejecuta en los siguientes pasos:
     -      entrada caracteres de espacio en blanco (como se especifica por la función isspace) se omiten, a menos que la especificación incluye un especificador '[', 'c' o 'n'.

y, en otros lugares, que describe isspace():

Los caracteres de espacio en blanco estándar son los siguientes: espacio ' ', la forma de alimentación '\f', nueva línea '\n', retorno de carro '\r', pestaña horizontal '\t', y la pestaña vertical .

Su mejor opción es utilizar fgets para obtener la línea (y esto puede ser protegido de desbordamiento de búfer muy fácilmente), a continuación, utilizar sscanf en la línea resultante.

La función scanf es una de esas que debe observar muy cautelosamente. El siguiente fragmento de código es a menudo uno que utilizo para manejar la entrada de línea:

#include <stdio.h> 
#include <string.h> 

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
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. 
    if (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; 
} 

 

// Test program for getLine(). 

int main (void) { 
    int rc; 
    char buff[10]; 

    rc = getLine ("Enter string> ", buff, sizeof(buff)); 
    if (rc == NO_INPUT) { 
     // Extra NL since my system doesn't output that on EOF. 
     printf ("\nNo input\n"); 
     return 1; 
    } 

    if (rc == TOO_LONG) { 
     printf ("Input too long [%s]\n", buff); 
     return 1; 
    } 

    printf ("OK [%s]\n", buff); 

    return 0; 
} 

Prueba con varias combinaciones:

pax> ./prog 
Enter string>[CTRL-D] 
No input 

pax> ./prog 
Enter string> a 
OK [a] 

pax> ./prog 
Enter string> hello 
OK [hello] 

pax> ./prog 
Enter string> hello there 
Input too long [hello the] 

pax> ./prog 
Enter string> i am pax 
OK [i am pax] 

Lo que yo haría es utilizar esta función para obtener una línea de forma segura, entonces simplemente use:

sscanf (buffer, "%f %f", &real, &img) 

para obtener los valores reales (y verificar el recuento).


De hecho, aquí es un completo programa que está más cerca de lo que quiere:

#include <stdio.h> 
#include <string.h> 

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
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. 
    if (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; 
} 

 

int main (void) { 
    int i = 1, rc; 
    char prompt[50], buff[50]; 
    float real, imag; 

    while (1) { 
     sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i); 
     rc = getLine (prompt, buff, sizeof(buff)); 
     if (rc == NO_INPUT) break; 
     if (*buff == '\0') break; 

     if (rc == TOO_LONG) { 
      printf ("** Input too long [%s]...\n", buff); 
     } 

     if (sscanf (buff, "%f %f", &real, &imag) == 2) { 
      printf ("Values were %f and %f\n", real, imag); 
      i++; 
     } else { 
      printf ("** Invalid input [%s]\n", buff); 
     } 
    } 

    return 0; 
} 

junto con una prueba de funcionamiento:

pax> ./testprog 

Enter real and imaginary for # 1: hello 
** Invalid input [hello] 

Enter real and imaginary for # 1: hello there 
** Invalid input [hello there] 

Enter real and imaginary for # 1: 1 
** Invalid input [1] 

Enter real and imaginary for # 1: 1.23 4.56 
Values were 1.230000 and 4.560000 

Enter real and imaginary for # 2: 

pax> _ 
+0

+1 El uso de 'scanf' para leer la entrada del usuario a menudo presenta problemas como este. Por lo general, es mucho menos propenso a errores simplemente leer la entrada como una cadena y luego analizar la cadena. – bta

2

Hay una manera de hacer lo que que desee utilizando simplemente scanf:

int readCoefficents(double complex *c) { 
    int i = 0; 
    double real; 
    double img; 
    char buf[2]; 
    while (scanf("%1[\n]", buf) == 0) {   // loop until a blank line or EOF 
     if (scanf("%lf %lf", &real, &img) == 2) // read two floats 
      c[i++] = real + img * I; 
     scanf("%*[^\n]");      // skip the rest of the line 
     scanf("%*1[\n]");      // and the newline 
    } 
    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
} 

Si el usuario sólo entra 1 flotador en una línea, se lee la siguiente línea para el segundo valor. Si se ingresa cualquier basura al azar, se saltará a una nueva línea e intentará nuevamente con la siguiente línea. De lo contrario, continuará leyendo pares de valores flotantes hasta que el usuario ingrese una línea en blanco o se alcance un EOF.

0

re solución PAXDIABLO: no funciona correctamente con línea vacía introducida por el usuario, por lo que esta línea se añadirá en su getLine función

if (strlen(buff) <= 1) return NO_INPUT; 

() después de la línea:

if (fgets (buff, sz, stdin) == NULL) 
    return NO_INPUT; 

Entonces se convertirá en:

... 
    if (strlen(buff) <= 1) return NO_INPUT; 
    if (fgets (buff, sz, stdin) == NULL) return NO_INPUT; 
.... 
Cuestiones relacionadas