2008-10-19 10 views
10

Ho avuto un sacco di problemi cercando di capire come usare scanf(). Sembra funzionare bene con numeri interi, essendo abbastanza diretto scanf("%d", &i).Parsing input con scanf in C

In cui sono in esecuzione problemi con scanf() nei loop che tentano di leggere l'input. Per esempio:

do { 
    printf("counter: %d: ", counter); 
    scanf("%c %c%d", &command, &prefix, &input); 
} while (command != 'q'); 
  1. Quando entro in una validamente ingresso strutturato come c P101, sembra ciclo di nuovo davanti a me chiedere conferma. Questo sembra accadere anche con un singolo:

    scanf("%c", &c) 
    

    in un ciclo while. Farà il ciclo due volte prima di richiamarmi di nuovo. Che cosa lo rende loop due volte e come lo interrompo?

  2. Quando inserisco una quantità inferiore di input che a livello di programmazione non avrebbe un altro carattere o numero come q, premere Invio sembra richiedere di inserire altro. Come posso ottenere scanf() per elaborare le voci sia a singolo che a doppio carattere?

risposta

20

Quando si immette "c P101", il programma riceve effettivamente "c P101\n". La maggior parte degli specificatori di conversione salta gli spazi bianchi iniziali inclusi i newline ma non lo è lo %c. La prima volta intorno a tutto fino a "\n" viene letto, la seconda volta il "\ n" viene letto in command, "c" viene letto in prefix e "P" è lasciato che non è un numero quindi la conversione fallisce e "P101\n" rimane nello stream. La prossima volta che "P" viene memorizzato nel comando, "1" viene memorizzato nel prefisso e 1 (dal rimanente "01") viene memorizzato in input con "\n" ancora sullo streaming per la volta successiva. Puoi risolvere questo problema inserendo uno spazio all'inizio della stringa di formato che salterà qualsiasi spazio bianco iniziale, inclusi i newline.

Una cosa simile sta accadendo per il secondo caso, quando si entra "q", "q\n" viene inserito nel flusso, la prima volta intorno al "q" viene letto, la seconda volta la "\n" viene letto , solo la terza chiamata è la seconda lettura "q", è possibile evitare nuovamente il problema aggiungendo un carattere spazio all'inizio della stringa di formato.

Un modo migliore per farlo sarebbe utilizzare qualcosa come fgets() per elaborare una riga alla volta e quindi utilizzare sscanf() per eseguire l'analisi.

+2

Ottima spiegazione, sospettavo che dovesse leggere il \ n, ma questo ha certamente complicato quello che pensavo fosse un semplice parsing. Analizzerò fgets e sscanf(). – zxcv

+0

Prenderesti in considerazione l'aggiunta di un paragrafo sulla verifica del valore di ritorno da 'scanf()' per assicurarti di ottenere ciò che ti aspettavi. A meno che tu non sia a conoscenza di una domanda precedente, è probabile che diventi la Q & A canonica per questo problema, ma la risposta canonica dovrebbe coprire tutti i punti ragionevoli e penso che il ritorno da 'scanf()' sia un punto ragionevole.Fai un commento mirato a me quando lo fai; Quindi rimuoverò questo commento e contrassegno il tuo commento per la rimozione (oppure puoi contrassegnarlo come obsoleto, ma mi piacerebbe sapere che hai apportato le modifiche). Grazie. –

1

Per la domanda 1, ho il sospetto che hai un problema con il printf(), dal momento che non v'è alcuna terminazione "\ n".

Il comportamento predefinito di printf consiste nel buffer di output fino a quando non ha una linea completa. Questo a meno che non si modifichi esplicitamente il buffering su stdout.

Per la domanda 2, hai appena raggiunto uno dei maggiori problemi con scanf(). A meno che il tuo input corrisponda esattamente alla stringa di scansione che hai specificato, i tuoi risultati non saranno come quelli che ti aspetti.

Se si dispone di un'opzione, si otterranno risultati migliori (e meno problemi di sicurezza) ignorando scanf() e facendo la propria analisi. Ad esempio, utilizzare fgets() per leggere un'intera riga in una stringa, quindi elaborare i singoli campi della stringa —, magari utilizzando anche sscanf().

+0

Hmm, non penso che sia così. anche con un \ n put, lo stamperà solo due volte prima che scanf mi chieda di nuovo. Ho persino provato a inserire printf prima e dopo lo scanf (all'interno del ciclo do-while) e l'anello due volte prima che scanf ricominciasse. Questo è con l'input di un singolo carattere, come s e return. – zxcv

-1

scanf fa schifo, per le ragioni che hai già scoperto. È molto meglio usare qualcosa per ottenere l'intera linea e poi analizzarla.

+0

Sono aperto ai suggerimenti ... cosa proponi è un'alternativa migliore? – zxcv

+0

Usa cosa? Dici qualcosa che fa schifo ma non fornire alternative? – Alfred

+0

fgets è la soluzione più semplice. Ma ci sono librerie C che lo fanno in modo sicuro. –

1

È davvero rotto! Non sapevo che

#include <stdio.h> 

int main(void) 
{ 
    int counter = 1; 
    char command, prefix; 
    int input; 

    do 
    { 
     printf("counter: %d: ", counter); 
     scanf("%c %c%d", &command, &prefix, &input); 
     printf("---%c %c%d---\n", command, prefix, input); 
     counter++; 
    } while (command != 'q'); 
} 
counter: 1: a b1 
---a b1--- 
counter: 2: c d2 
--- 
c1--- 
counter: 3: e f3 
---d 21--- 
counter: 4: ---e f3--- 
counter: 5: g h4 
--- 
g3--- 

L'uscita sembra adattarsi con la risposta di Robert.

0

Forse usando un ciclo while, non una do ... while ciclo sarà di aiuto. In questo modo la condizione viene testata prima dell'esecuzione del codice. Provate il seguente frammento di codice:

while(command != 'q') 
    { 
     //statements 
    } 

Inoltre, se si conosce la lunghezza della stringa prima del tempo, 'per' i loop possono essere molto più facile da lavorare rispetto 'mentre' loop. Ci sono anche alcuni modi più complicati per determinare dinamicamente anche la lunghezza.

Come rant finale: scanf() non "succhiare". Fa quello che fa e questo è tutto. fgets() è molto pericoloso (sebbene conveniente per le applicazioni senza rischi), dal momento che non esegue alcun controllo sull'input in modo nativo. È MOLTO comunemente noto come punto di exploit, in particolare attacchi di overflow del buffer, sovrascrittura dello spazio nei registri non allocati per quella variabile. Quindi, se scegli di usarlo, dedica un po 'di tempo a sistemare alcuni corretti errori di correzione/correzione.

Spero che questo aiuti qualcuno in futuro, dal momento che presumo di averlo già gestito! ;)

+0

Perché dici che 'fgets' è pericoloso? Forse lo stai confondendo con 'get'? – interjay

+0

errore mio, volevo dire 'gets()', codifica a tarda notte! ... – jap3r