Pergunta

A seguir C++ código usa um ifstream objeto para ler números inteiros de um arquivo de texto (que tem um número por linha) até atingir EOF.Por que ele lê o número inteiro na última linha duas vezes?Como consertar isto?

Código:

#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;
}

entrada.txt:

10  
20  
30

Saída:

10  
20  
30  
30

Observação:Ignorei todo o código de verificação de erros para manter o trecho de código pequeno.O comportamento acima é visto no Windows (Visual C++), cygwin (gcc) e Linux (gcc).

Foi útil?

Solução

Basta acompanhar de perto a cadeia de eventos.

  • Pegue 10
  • Pegue 20
  • Pegue 30
  • Pegue EOF

Veja a penúltima iteração.Você pegou 30 e continuou verificando o EOF.Você não atingiu o EOF porque a marca EOF ainda não foi lida (falando "binaricamente", sua localização conceitual é logo após a linha 30).Portanto, você segue para a próxima iteração.x ainda é 30 da iteração anterior.Agora você lê o stream e obtém EOF.x permanece 30 e o ios::eofbit é aumentado.Você envia para stderr x (que é 30, assim como na iteração anterior).Em seguida, você verifica EOF na condição do loop e, desta vez, está fora do loop.

Experimente isto:

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

A propósito, há outro bug no seu código.Você já tentou executá-lo em um arquivo vazio?O comportamento que você obtém é exatamente pelo mesmo motivo.

Outras dicas

Gosto deste exemplo, que por enquanto deixa de fora a verificação que você poderia adicionar dentro do bloco while:

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

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

Não tenho certeza de quão seguro é...

Existe uma abordagem alternativa para isso:

#include <iterator>
#include <algorithm>

// ...

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

O padrão EOF precisa de uma leitura principal para 'inicializar' o processo de verificação EOF.Considere que o arquivo vazio não terá inicialmente seu EOF definido até a primeira leitura.A leitura principal capturará o EOF neste caso e pulará completamente o loop.

O que você precisa lembrar aqui é que você não obtém o EOF até a primeira tentativa de ler os dados disponíveis do arquivo.A leitura da quantidade exata de dados não sinalizará o EOF.

Devo salientar que se o arquivo estivesse vazio, seu código fornecido teria sido impresso, pois o EOF teria impedido que um valor fosse definido como x na entrada no loop.

  • 0

Então adicione uma leitura principal e mova a leitura do loop para o final:

int x;

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

Sem muitas modificações no código original, ele poderia se tornar:

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

mas prefiro as outras duas soluções acima em geral.

int x;
ifile >> x

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

No final da última linha, você tem um caractere de nova linha, que não é lido pelo operador >> e não é o fim do arquivo.Por favor, faça uma experiência e exclua a nova linha (o último caractere do arquivo) - você não obterá a duplicação.Para ter um código flexível e evitar efeitos indesejados basta aplicar qualquer solução dada por outros usuários.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top