2009-05-14 17 views
6

In an answer ci fu una dichiarazione interessante:. "E 'quasi sempre una cattiva idea di utilizzare la funzione di fscanf() come si può lasciare il puntatore del file in un luogo sconosciuto in caso di fallimento Io preferisco usare fgets() per ottenere ogni riga e poi sscanf(). "Quando/perché è una cattiva idea usare la funzione fscanf()?

Potresti approfondire quando/perché potrebbe essere meglio usare fgets() e sscanf() per leggere qualche file?

risposta

13

Immaginate un file con tre linee:

1 
    2b 
    c 

Uso fscanf() di leggere interi, la prima linea dovrebbe leggere bene, ma sulla seconda riga fscanf() si lascerebbe al 'b', non è sicuro che cosa fare da li. Avresti bisogno di un meccanismo per oltrepassare l'input spazzatura per vedere la terza linea.

Se fate una fgets() e sscanf(), si può garantire che il puntatore del file si muove una linea alla volta, che è un po 'più facile da affrontare. In generale, dovresti comunque guardare l'intera stringa per riportare eventuali caratteri strani al suo interno.

preferisco quest'ultimo approccio me, anche se io non sarei d'accordo con l'affermazione che "è quasi sempre una cattiva idea di utilizzare fscanf()" ... fscanf() è perfettamente bene per la maggior parte delle cose.

+1

Si prega di cambiare 'gets()' a 'fgets()'. 'gets()' non dovrebbe mai essere usato. – Wiz

+0

Deve essere stato un refuso :) Grazie per averlo capito. –

0

Fondamentalmente, non c'è modo di dire che la funzione non è andare fuori limite per l'area di memoria che hai assegnato per esso.

Un numero di sostituzioni è venuto fuori, come fnscanf, che è un tentativo di correggere quelle funzioni specificando un limite massimo per il lettore da scrivere, consentendo così di non traboccare.

+1

mentre i buffer overflow sono un problema con la famiglia di funzioni scanf(), non sono correlati al problema richiesto qui. -1 – Sparr

+1

"Potresti spiegare perché potrebbe essere meglio usare fgets() e sscanf() per leggere qualche file." Stavo espandendo la sua domanda. Rifiuto il tuo troppo ambiguo "-1" – cyberconte

+1

, prendo "espandi sul perché" per significare che la tua risposta dovrebbe essere basata sulla premessa già presentata, che è il problema del puntatore del file. Se voleva ALTRE ragioni, non avrebbe collegato all'origine della domanda, né avrebbe citato la parte rilevante di essa. – Sparr

2

Quando fscanf() non riesce, a causa di un errore di input o di un errore di corrispondenza, il puntatore del file (ovvero la posizione nel file da cui verrà letto il byte successivo) viene lasciato in una posizione diversa da quella in cui si trova si sarebbe avuto il fscanf() riuscito. Questo è tipicamente indesiderabile nelle letture sequenziali di file. La lettura di una riga alla volta comporta che l'input del file sia prevedibile, mentre gli errori di linea singola possono essere gestiti singolarmente.

1

È quasi sempre una cattiva idea utilizzare la funzione fscanf() in quanto può lasciare il puntatore del file in una posizione sconosciuta in caso di errore. Preferisco usare fgets() per ottenere ogni riga e quindi sscanf().

È sempre possibile utilizzare ftell() per scoprire posizione corrente nel file, quindi decidere cosa fare da lì. In sostanza, se sai cosa puoi aspettarti, sentiti libero di usare fscanf().

4

Il caso in cui questo entra in gioco è quando si confrontano i caratteri letterali. Supponiamo di avere:

int n = fscanf(fp, "%d,%d", &i1, &i2); 

considerare due possibili ingressi "323,A424" e "323A424".

In entrambi i casi, fscanf() restituirà 1 e il successivo carattere letto sarà uno 'A'.Non c'è modo di determinare se la virgola è stata abbinata o meno.

Ciò detto, ciò è importante solo se è importante trovare la fonte effettiva dell'errore. Nei casi in cui la conoscenza di errori di input non validi è sufficiente, fscanf() è effettivamente superiore alla scrittura di codice di analisi personalizzato.

2

ci sono due ragioni:

  • scanf() può lasciare stdin in uno stato che è difficile da prevedere; questo rende difficile il recupero degli errori se non impossibile (questo è meno un problema con fscanf()); e
  • L'intera famiglia scanf() accetta i puntatori come argomenti, ma non ha limiti di lunghezza, in modo da poter sovrascrivere un buffer e modificare variabili non correlate che si trovano dopo il buffer, causando errori di corruzione della memoria apparentemente casuali che sono molto difficili da capire, trovare, e debug, in particolare per i programmatori meno esperti C.

debuttante C i programmatori sono spesso confusi circa puntatori e “Indirizzo-di” operatore, e spesso omette il & dove è necessario, o aggiungere “per buona misura” dove non è. Ciò causa segoults "casuali" che possono essere difficili da trovare per loro. Questa non è la colpa di scanf(), quindi la lascio fuori dalla mia lista, ma vale la pena tenerla a mente.

dopo 23 anni, ho ancora ricordo che sia un dolore enorme quando ho iniziato C programmazione e non sapevo come riconoscere ed eseguire il debug di questi tipi di errori, e (come qualcuno che ha trascorso anni di insegnamento C per i principianti) è molto difficile spiegarli a un novizio che non ha ancora capito i puntatori e impila.

Chiunque consiglia scanf() a un principiante C programmatore deve essere frodato senza pietà.

OK, forse non senza pietà, ma una sorta di fustigazione è sicuramente in ordine; o)

+0

L'istruzione "prendere i puntatori come argomenti, ma nessun limite di lunghezza" è fuorviante: per la maggior parte dei tipi, le dimensioni sono fisse ('% i','% d', '% lf') quindi i limiti di lunghezza non sono necessari. Un'eccezione è per la lettura delle stringhe con '% s'.Ma anche con questo, un limite può essere specificato aggiungendo un numero tra '%' e 's':'% 99s' per una stringa di caratteri dichiarata come 'char s [100]'. –

Problemi correlati