2012-02-14 3 views
34

Molte persone hanno affermato che lo scanf non deve essere utilizzato in "programma più serio", come con getline.Qual è il modo migliore per ottenere input dall'utente in C?

Ho iniziato a perdersi: se ogni funzione di input che ho incontrato tra le persone diceva che non dovrei usare nessuno di loro, allora cosa dovrei usare? C'è un modo più "standard" per ottenere input di cui non sono a conoscenza?

+1

che molto dipende da ciò che fa il tuo programma di fare. Vi raccomando (quasi in ogni situazione) di usare 'scanf', e controllarne il valore di ritorno. – asaelr

+1

'scanf' è il metodo standard per ottenere input formattati in C, e' fgets'/'fgetc' è la funzione standard consigliata per ottenere intere linee o singoli caratteri. La maggior parte delle altre funzioni sono non standard o specifiche della piattaforma. –

+1

Non c'è motivo per non usare 'getline' in un programma serio. Se la tua piattaforma non ce l'ha, puoi implementarla in termini di 'fgets' e' malloc'. –

risposta

33

Generalmente, fgets() è considerato una buona opzione. Legge intere righe in un buffer e da lì puoi fare ciò che ti serve. Se si desidera un comportamento come scanf(), è possibile passare le stringhe che si leggono su sscanf().

Il vantaggio principale di questo, è che se la stringa non riesce a convertire, è facile da recuperare, mentre con scanf() si è lasciato con ingresso sul stdin cui è necessario svuotare. Inoltre, non si finisce nel mix di input diretto alla linea con scanf(), che provoca mal di testa quando cose come stdin lasciano normalmente ai nuovi programmatori che credono che le chiamate di input siano state ignorate del tutto.

Qualcosa di simile potrebbe essere di vostro gradimento:

char line[256]; 
int i; 
if (fgets(line, sizeof(line), stdin)) { 
    if (1 == sscanf(line, "%d", &i)) { 
     /* i can be safely used */ 
    } 
} 

Sopra si dovrebbe notare che fgets() rendimenti NULL su EOF o errori, che è il motivo per cui ho avvolto in un if. La chiamata sscanf() restituisce il numero di campi che sono stati convertiti correttamente.

Ricordare che fgets() potrebbe non leggere un'intera riga se la riga è più grande del buffer, che in un programma "serio" è sicuramente una cosa da considerare.

+1

Che ne dici di aggiungere 'fflush (stdin)' subito dopo 'fgets()' per assicurarci che un buffer di input troppo pieno (più di 255 caratteri) non influenzerà le successive chiamate a 'fgets()'? – Twonky

+1

@Twonky: 'fflush (stdin)' non è definito per quanto riguarda C (ma * fa * ciò che suggerisci su alcune piattaforme, però). Ma sì, potrebbe essere reso più robusto al riguardo. – FatalError

11

Per un input semplice in cui è possibile impostare un limite fisso sulla lunghezza dell'input, si consiglia di leggere i dati dal terminale con fgets().

Questo perché fgets() consente di specificare la dimensione del buffer (al contrario di gets(), che proprio per questo dovrebbe praticamente mai essere utilizzato per leggere l'input da esseri umani):

char line[256]; 

if(fgets(line, sizeof line, stdin) != NULL) 
{ 
    /* Now inspect and further parse the string in line. */ 
} 

Ricorda che manterrà ad es il personaggio oi personaggi in coda, che potrebbero essere sorprendenti.

UPDATE: Come sottolineato in un commento, non c'è un'alternativa migliore, se stai bene con ottenere la responsabilità per il monitoraggio della memoria: getline(). Questa è probabilmente la migliore soluzione generica per il codice POSIX, poiché non ha limiti statici sulla lunghezza delle righe da leggere.

+0

"input semplice in cui è possibile impostare un limite fisso sulla lunghezza dell'input" non suona come programmi "gravi". La funzione POSIX 'getline' è molto più flessibile. –

+0

@larsmans Grande cattura, grazie per averlo indicato. – unwind

+0

Se potessi inserire 2 vincitori per la mia domanda, saresti il ​​secondo, a causa del dettaglio getline() e del comportamento di chi ti ha spiegato. Ma la risposta di FatalError ha avuto maggiori dettagli su come usare questa tecnica. – user1115057

2

Utilizzare fgets per ottenere i dati e utilizzare sscanf (o un altro metodo) per interpretarli.

Vedere questa pagina per sapere il motivo per cui è meglio usare fgets + sscanf piuttosto che scanf

http://c-faq.com/stdio/scanfprobs.html

5

Ci sono diversi problemi con l'utilizzo scanf:

  • lettura del testo con una pianura Lo specificatore di conversione %s ha lo stesso rischio dell'uso di gets(); se l'utente digita una stringa che è più lunga di quanto il buffer di destinazione è dimensionato per contenere, si verificherà un sovraccarico del buffer;

  • se si utilizza %d o %f leggere input numerico, certi schemi cattivi non possono essere catturati e respinte del tutto - se stai leggendo un intero con %d e l'utente digita "12r4", scanf convertirà e assegnare il 12 lasciando lo r4 nello stream di input per ripulire la prossima lettura;

  • alcuni specificatori di conversione salta gli spazi bianchi iniziali, altri no, e la mancata considerazione di ciò può portare a problemi in cui alcuni input vengono saltati completamente;

In sostanza, ci vuole un sacco di sforzo in più per proiettile letture utilizzando scanf.

Una buona alternativa è quello di leggere tutto l'input come testo utilizzando fgets(), e poi tokenize e convertire l'ingresso utilizzando sscanf o combinazioni di strtok, strtol, strtod, ecc

+1

Grazie, ora so qual è la ragione per cui la gente dice che scanf può essere cattivo. – user1115057