Problemi con la lettura di una riga usando fscanf ()

Sto provando a leggere una riga usando il seguente codice:

while(fscanf(f, "%[^\n\r]s", cLine) != EOF ) { /* do something with cLine */ } 

Ma in qualche modo ottengo solo la prima riga ogni volta. È un modo sbagliato di leggere una riga? Cosa devo aggiustare per farlo funzionare come previsto?

È quasi sempre una ctriggers 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 poi sscanf() . È quindi ansible continuare a esaminare la riga letta come si ritiene opportuno. Qualcosa di simile a:

 #define LINESZ 1024 char buff[LINESZ]; FILE *fin = fopen ("infile.txt", "r"); if (fin != NULL) { while (fgets (buff, LINESZ, fin)) { /* Process buff here. */ } fclose (fin); } 

fgets() sembra essere quello che stai cercando di fare, leggendo in una stringa finché non incontrerai un carattere di nuova riga.

Se vuoi leggere un file riga per riga (Qui, separatore di riga == ‘\ n’) basta fare ciò:

 #include  #include  #include  int main(int argc, char **argv) { FILE *fp; char *buffer; int ret; // Open a file ("test.txt") if ((fp = fopen("test.txt", "r")) == NULL) { fprintf(stdout, "Error: Can't open file !\n"); return -1; } // Alloc buffer size (Set your max line size) buffer = malloc(sizeof(char) * 4096); while(!feof(fp)) { // Clean buffer memset(buffer, 0, 4096); // Read a line ret = fscanf(fp, "%4095[^\n]\n", buffer); if (ret != EOF) { // Print line fprintf(stdout, "%s\n", buffer); } } // Free buffer free(buffer); // Close file fclose(fp); return 0; } 

Godere 🙂

Se provi while( fscanf( f, "%27[^\n\r]", cLine ) == 1 ) potresti avere un po ‘più di fortuna. Le tre modifiche dal tuo originale:

  • limite di lunghezza che viene letto – Ho usato 27 qui come esempio, e sfortunatamente la famiglia scanf() richiede letteralmente la larghezza del campo nella stringa di formato e non può usare il meccanismo * che può printf() il printf() il valore in
  • sbarazzarsi della s nella stringa di formato – %[ è lo specificatore di formato per “tutti i caratteri che corrispondono o non corrispondono a un set”, e il set è terminato da a ] da solo
  • confronta il valore di ritorno con il numero di conversioni che ti aspetti di accadere (e per facilità di gestione, assicurati che il numero sia 1)

Detto questo, otterrete lo stesso risultato con meno dolore usando fgets() per leggere una riga che si adatta al vostro buffer.

Usando fscanf per leggere / tokenizzare un file risulta sempre un codice fragile o dolore e sofferenza. Leggendo una linea, e la tokenizzazione o la scansione di quella linea è sicuro ed efficace. Ha bisogno di più linee di codice – il che significa che ci vuole più tempo a pensare a cosa si vuole fare (e bisogna gestire una dimensione del buffer di input finita) – ma dopo quella vita puzza di meno.

Non combattere fscanf. Basta non usarlo. Mai.

Mi sembra che tu stia cercando di usare gli operatori di espressioni regolari nella tua stringa fscanf. La stringa [^\n\r] non significa nulla per fscanf, motivo per cui il tuo codice non funziona come previsto.

Inoltre, fscanf () non restituisce EOF se l’elemento non corrisponde. Piuttosto, restituisce un numero intero che indica il numero di corrispondenze, che nel tuo caso è probabilmente zero. EOF viene restituito solo alla fine dello stream o in caso di errore. Quindi, quello che sta accadendo nel tuo caso è che la prima chiamata a fscanf () continua fino alla fine del file cercando una stringa corrispondente, quindi restituisce 0 per farti sapere che non è stata trovata alcuna corrispondenza. La seconda chiamata restituisce quindi EOF perché è stato letto l’intero file.

Infine, si noti che l’operatore di formato% s scanf acquisisce solo il successivo carattere di spazi bianchi, quindi non è necessario escludere \ n o \ r in ogni caso.

Consultare la documentazione di fscanf per ulteriori informazioni: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/

Il tuo ciclo ha diversi problemi. Hai scritto:

 while( fscanf( f, "%[^\n\r]s", cLine ) != EOF ) /* do something */; 

Alcune cose da considerare:

  1. fscanf () restituisce il numero di elementi memorizzati. Può restituire EOF se legge oltre la fine del file o se l’handle del file ha un errore. È necessario distinguere un ritorno valido di zero nel qual caso non vi è alcun nuovo contenuto nel buffer cLine da una lettura corretta.

  2. Si ha un problema quando si verifica un errore di corrispondenza perché è difficile prevedere dove ora l’handle del file punta nello stream. Ciò rende più difficile eseguire il recupero da una partita fallita di quanto ci si potrebbe aspettare.

  3. Lo schema che hai scritto probabilmente non fa ciò che intendi. Corrisponde a qualsiasi numero di caratteri che non siano CR o LF e quindi si aspettano di trovare s letterali.

  4. Non hai protetto il tuo buffer da un overflow. Qualsiasi numero di caratteri può essere letto dal file e scritto nel buffer, indipendentemente dalla dimensione allocata a quel buffer. Questo è un errore purtroppo comune, che in molti casi può essere sfruttato da un utente malintenzionato per eseguire codice arbitrario scelto dagli attaccanti.

  5. A meno che non si richieda espressamente che f sia aperto in modalità binaria, la traduzione finale della linea avverrà nella libreria e generalmente non si vedranno mai caratteri CR, e di solito non in file di testo.

Probabilmente vuoi un loop più simile al seguente:

 while(fgets(cLine, N_CLINE, f)) { /* do something */ ; } 

dove N_CLINE è il numero di byte disponibili nel buffer che inizia una cLine .

La funzione fgets() è un modo molto preferito per leggere una riga da un file. Il secondo parametro è la dimensione del buffer e legge fino a 1 in meno di quella dimensione di byte dal file nel buffer. Termina sempre il buffer con un carattere nul modo che possa essere tranquillamente passato ad altre funzioni di stringa C.

Si ferma sul primo della fine del file, newline o buffer_size-1 byte letti.

Lascia il carattere di nuova riga nel buffer e ciò consente di distinguere una singola riga più lunga del buffer da una riga più breve del buffer.

Restituisce NULL se nessun byte è stato copiato a causa della fine del file o di un errore e altrimenti il ​​puntatore al buffer. Potresti voler usare feof() e / o ferror() per distinguere questi casi.

Penso che il problema con questo codice sia dovuto al fatto che quando leggi con% [^ \ n \ r] s, infatti, stai leggendo fino a raggiungere ‘\ n’ o ‘\ r’, ma non stai leggendo ‘\ n ‘o’ \ r ‘anche. Quindi è necessario ottenere questo personaggio prima di leggere nuovamente con fscanf al ciclo. Fai qualcosa del genere:

 do{ fscanf(f, "%[^\n\r]s", cLine) != EOF /* Do something here */ }while(fgetc(file) != EOF)