Lendo do arquivo de texto até que o EOF repita a última linha [duplicado]
Pergunta
Essa pergunta já tem resposta aqui:
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).
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.