2010-09-16 17 views
6

Se immetto 5 5 nel terminale, premere invio, quindi premere di nuovo invio, Voglio uscire dal ciclo.Ottieni scanf per uscire quando legge una nuova riga?

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; 
} 

Ovviamente, che il codice non sta facendo il lavoro per me (e sì, so che c'è un buffer overflow in attesa di accadere).

scanf non si chiuderà a meno che non digiti una lettera (o una stringa non numerica, non di spaziatura). Come faccio a convincere scanf a uscire dopo aver letto una riga vuota?

+0

Nel caso in cui si aveva bisogno di più motivi per evitare di utilizzare 'scanf': http://c-faq.com/stdio/scanfprobs.html – jamesdlin

risposta

7

Utilizzare fgets per leggere l'input della console:

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

Ti concedo il credito da quando hai menzionato prima fgets e sscanf. – Matt

+0

Grazie, appena realizzato c probabilmente non ti permetterà di dichiarare il buffer all'interno del ciclo while ma il concetto è lì. – MattSmith

+2

Quella dichiarazione è perfettamente a posto. – jamesdlin

9

Il problema specifico che si sta avendo è che una stringa di formato scanf di %f salterà spazi bianchi (compresa fine riga) finché non trova un carattere effettivo scansione. Dalla serie C99:

Una specifica di conversione viene eseguita nei seguenti passi:
     -      ingresso spazi vuoti (come specificato dalla funzione isspace) vengono ignorati, a meno che la specifica comprende a '[', 'c' o 'n' specificatore.

e, altrove, descrivendo isspace():

Gli spazi vuoti standard sono i seguenti: spazio ' ', forma mangimi '\f', new-line '\n', ritorno carrello '\r', scheda orizzontale '\t', e scheda verticale .

La cosa migliore è quella di utilizzare fgets per ottenere la linea (e questo può essere protetto da buffer overflow molto facilmente), quindi utilizzare sscanf sulla linea risultante.

La funzione scanf è una di quelle che dovresti guardare con molta cautela. Il seguente pezzo di codice è uno che uso spesso per gestire l'ingresso di linea:

#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; 
} 

test con diverse combinazioni:

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] 

Quello che vorrei fare è usare questa funzione per ottenere una linea in modo sicuro, quindi utilizzare semplicemente:

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

per ottenere i valori effettivi (e controllare il conteggio).


In realtà, ecco un programma completo, che è più vicino a ciò che si vuole:

#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; 
} 

insieme ad una corsa di prova:

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 L'uso di 'scanf' per leggere l'input dell'utente spesso introduce problemi come questo. Di solito è molto meno incline agli errori di leggere semplicemente l'input come stringa e quindi analizzare la stringa. – bta

2

C'è un modo per fare cosa si desidera utilizzare solo 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; 
} 

Se l'utente inserisce solo 1 galleggiante su una linea, si leggerà la riga successiva per il secondo valore. Se viene immessa una spazzatura casuale, salta a una nuova riga e riprova con la riga successiva. In caso contrario, continuerà a leggere coppie di valori float fino a quando l'utente non immetterà una riga vuota o un EOF viene raggiunto.

0

re soluzione PAXDIABLO: non funziona correttamente con la linea VUOTO immesso dall'utente, in modo da questa linea è aggiunto nella tua GetLine funzione

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

() dopo la riga:

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

così diventerà:

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