Domanda

Il seguente C++ il codice utilizza a ifstream oggetto per leggere numeri interi da un file di testo (che ha un numero per riga) finché non colpisce EOF.Perché legge due volte il numero intero sull'ultima riga?Come risolvere questo problema?

Codice:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ifstream iFile("input.txt");    // input.txt has integers, one per line

    while (!iFile.eof())
    {
        int x;
        iFile >> x;
        cerr << x << endl;
    }

    return 0;
}

input.txt:

10  
20  
30

Produzione:

10  
20  
30  
30

Nota:Ho saltato tutto il codice di controllo degli errori per mantenere piccolo lo snippet di codice.Il comportamento sopra riportato è visibile su Windows (Visual C++), cygwin (gcc) e Linux (gcc).

È stato utile?

Soluzione

Basta seguire da vicino la catena degli eventi.

  • Prendine 10
  • Prendine 20
  • Prendine 30
  • Prendi EOF

Guarda la penultima iterazione.Ne hai presi 30, poi hai continuato a controllare l'EOF.Non hai raggiunto EOF perché il segno EOF non è stato ancora letto (in termini "binarici", la sua posizione concettuale è subito dopo la riga 30).Pertanto si passa all'iterazione successiva.x è ancora 30 dall'iterazione precedente.Ora leggi dallo stream e ottieni EOF.x rimane 30 e ios::eofbit viene generato.L'output è su stderr x (che è 30, proprio come nell'iterazione precedente).Successivamente controlli la presenza di EOF nella condizione del ciclo e questa volta sei fuori dal ciclo.

Prova questo:

while (true) {
    int x;
    iFile >> x;
    if( iFile.eof() ) break;
    cerr << x << endl;
}

A proposito, c'è un altro bug nel tuo codice.Hai mai provato a eseguirlo su un file vuoto?Il comportamento che ottieni è esattamente per lo stesso motivo.

Altri suggerimenti

Mi piace questo esempio, che per ora tralascia il controllo che potresti aggiungere all'interno del blocco while:

ifstream iFile("input.txt");        // input.txt has integers, one per line
int x;

while (iFile >> x) 
{
    cerr << x << endl;
}

Non sono sicuro di quanto sia sicuro...

Esiste un approccio alternativo a questo:

#include <iterator>
#include <algorithm>

// ...

    copy(istream_iterator<int>(iFile), istream_iterator<int>(),
         ostream_iterator<int>(cerr, "\n"));

Il modello EOF necessita di una lettura primaria per eseguire il bootstrap del processo di controllo EOF.Considera che il file vuoto inizialmente non avrà il suo EOF impostato fino alla prima lettura.La lettura principale catturerà l'EOF in questo caso e salterà completamente il ciclo.

Ciò che devi ricordare qui è che non ottieni l'EOF fino al primo tentativo di leggere oltre i dati disponibili del file.La lettura della quantità esatta di dati non contrassegnerà l'EOF.

Dovrei sottolineare che se il file fosse vuoto, il codice fornito sarebbe stato stampato poiché EOF avrebbe impedito che un valore fosse impostato su x all'ingresso nel ciclo.

  • 0

Quindi aggiungi una lettura principale e sposta la lettura del ciclo alla fine:

int x;

iFile >> x; // prime read here
while (!iFile.eof()) {
    cerr << x << endl;
    iFile >> x;
}

Senza troppe modifiche al codice originale, potrebbe diventare:

while (!iFile.eof())
{  
    int x;
    iFile >> x;
    if (!iFile.eof()) break;
    cerr << x << endl;
}

ma preferisco le altre due soluzioni sopra in generale.

int x;
ifile >> x

while (!iFile.eof())
{  
    cerr << x << endl;        
    iFile >> x;      
}

Alla fine dell'ultima riga, hai un carattere di nuova riga, che non viene letto dall'operatore >> e non è una fine del file.Per favore, fai un esperimento e cancella la nuova riga (l'ultimo carattere nel file): non otterrai la duplicazione.Per avere un codice flessibile ed evitare effetti indesiderati basta applicare qualsiasi soluzione fornita da altri utenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top