Acabei de encontrar um comentário nesta resposta dizendo que o uso iostream::eof
em uma condição de loop é "quase certamente errado". Eu geralmente uso algo como while(cin>>n)
- o que eu acho implicitamente verifica EOF.
Por que a verificação de eof está explicitamente usando while (!cin.eof())
errado?
Como é diferente de usar scanf("...",...)!=EOF
em C (que eu costumo usar sem problemas)?
scanf(...) != EOF
também não funcionará em C, porquescanf
retorna o número de campos analisados e atribuídos com êxito. A condição correta éscanf(...) < n
onden
está o número de campos na string de formato.EOF
se o final do arquivo for encontrado antes da primeira conversão do campo (bem-sucedida ou não). Se o final do arquivo for alcançado entre os campos, ele retornará o número de campos convertidos e armazenados com êxito. O que faz a comparaçãoEOF
errada..eof()
depois que o loop termina.while(fail)
loop termina com uma falha real e um Eof. Pense se você precisar de 3 ints por iteração (digamos que esteja lendo um ponto xyz ou algo assim), mas há, erroneamente, apenas duas ints no fluxo.Respostas:
Porque
iostream::eof
só retornarátrue
depois de ler o final do fluxo. Isso não indica que a próxima leitura será o fim do fluxo.Considere isso (e assuma que a próxima leitura será no final do fluxo):
Contra isso:
E na sua segunda pergunta: porque
é o mesmo que
e não é o mesmo que
fonte
int
s oustd::string
s ou similar, o bit EOF é definido quando você extrai o mesmo antes do final e a extração atinge o final. Você não precisa ler novamente. O motivo de não ser definido ao ler arquivos é porque há um extra\n
no final. Eu cobri isso em outra resposta . A leitura dechar
s é uma questão diferente porque extrai apenas uma de cada vez e não continua chegando ao fim.while (!eof())
não "funcionará" emint
s oustd::string
s quando a entrada estiver totalmente vazia, portanto, mesmo sabendo que não há\n
cuidados finais , é necessário."Hello"
(sem espaços em branco à direita ou\n
) e astd::string
for extraído, ele extrairá as letras deH
parao
, interromperá a extração e então não defina o bit EOF. De fato, ele definiria o bit EOF porque foi o EOF que interrompeu a extração. Apenas esperando esclarecer isso para as pessoas.Conclusão: com o manuseio adequado do espaço em branco, a seguir, é
eof
possível usar (e até ser mais confiável do quefail()
a verificação de erros):( Agradecemos a Tony D pela sugestão de destacar a resposta. Veja o comentário abaixo para um exemplo de por que isso é mais robusto. )
O principal argumento contra o uso
eof()
parece estar faltando uma sutileza importante sobre o papel do espaço em branco. Minha proposição é que, verificareof()
explicitamente não apenas não está " sempre errado " - o que parece ser uma opinião predominante nesse e nos tópicos similares -, mas com o manuseio adequado do espaço em branco, ele fornece um ambiente mais limpo e confiável. tratamento de erros e é a solução sempre correta (embora não necessariamente a mais difícil).Para resumir o que está sendo sugerido como a ordem de finalização e leitura "adequada", é o seguinte:
A falha devido à tentativa de leitura além de eof é tomada como condição de finalização. Isso significa que não há uma maneira fácil de distinguir entre um fluxo bem-sucedido e um que realmente falha por outros motivos que não o eof. Tome os seguintes fluxos:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
termina com um conjuntofailbit
para todas as três entradas. No primeiro e terceiro,eofbit
também está definido. Portanto, após o loop, é necessário uma lógica extra muito feia para distinguir uma entrada adequada (1ª) das impróprias (2ª e 3ª).Visto que, faça o seguinte:
Aqui,
in.fail()
verifica se, desde que haja algo para ler, é o correto. Seu objetivo não é um mero terminador de loop while.Até aí tudo bem, mas o que acontece se houver espaço à direita no fluxo - o que parece ser a principal preocupação contra
eof()
o terminador?Não precisamos renunciar ao tratamento de erros; apenas coma o espaço em branco:
std::ws
pula qualquer espaço potencial (zero ou mais) à direita no fluxo enquanto define oeofbit
, e não ofailbit
. Portanto,in.fail()
funciona conforme o esperado, desde que haja pelo menos um dado para ler. Se todos os fluxos em branco também forem aceitáveis, o formato correto será:Resumo: Uma construção adequada
while(!eof)
não é apenas possível e incorreta, mas permite que os dados sejam localizados no escopo e fornece uma separação mais limpa da verificação de erros dos negócios, como de costume. Dito isto,while(!fail)
é indiscutivelmente um idioma mais comum e conciso, e pode ser preferido em cenários simples (dados únicos por tipo de leitura).fonte
eofbit
efailbit
estão configurados, no outro apenasfailbit
está configurado. Você só precisa de teste que uma vez após o loop tenha terminado, não em cada iteração; ele sairá do loop apenas uma vez; portanto, você só precisa verificar por que ele saiu do loop uma vez.while (in >> data)
funciona bem para todos os fluxos em branco.!eof & fail
loop passado. Há casos em que não se pode confiar nisso. Veja o comentário acima ( goo.gl/9mXYX ). De qualquer forma, não estou propondoeof
-check como a sempre-melhor alternativa. Estou apenas dizendo que é uma maneira possível e (em alguns casos mais apropriada) de fazer isso, em vez de "certamente errado!" como tende a ser reivindicado por aqui no SO.stream >> my_int
onde o fluxo contém, por exemplo, "-":eofbit
efailbit
está conjunto. Isso é pior do que ooperator>>
cenário, onde a sobrecarga fornecida pelo usuário tem pelo menos a opção de limpareofbit
antes de retornar para ajudar no suporte aowhile (s >> x)
uso. De maneira mais geral, essa resposta pode ser usada como limpeza - apenas a finalwhile( !(in>>ws).eof() )
é geralmente robusta e está enterrada no final.Porque se os programadores não escrevem
while(stream >> n)
, eles possivelmente escrevem o seguinte:Aqui está o problema: você não pode ficar
some work on n
sem verificar primeiro se a leitura do fluxo foi bem-sucedida, porque, se não tivesse êxito, osome work on n
resultado seria indesejável.A questão toda é que,
eofbit
,badbit
, oufailbit
são definidas depois é feita uma tentativa de ler a partir do fluxo. Portanto, sestream >> n
falhar, entãoeofbit
,badbit
oufailbit
será definido imediatamente, será mais idiomático se você escreverwhile (stream >> n)
, porque o objeto retornado serástream
convertidofalse
se houver alguma falha na leitura do fluxo e, consequentemente, o loop for interrompido. E convertetrue
se a leitura foi bem-sucedida e o loop continua.fonte
n
, o programa também pode cair em um loop infinito , se a operação de fluxo com falha não consumir nenhuma entrada.As outras respostas explicaram por que a lógica está errada
while (!stream.eof())
e como corrigi-la. Eu quero focar em algo diferente:Em termos gerais, a verificação de
eof
apenas está errada porque a extração do fluxo (>>
) pode falhar sem atingir o final do arquivo. Se você tem, por exemplo,int n; cin >> n;
e o fluxo contémhello
, entãoh
não é um dígito válido, portanto a extração falhará sem atingir o final da entrada.Esse problema, combinado com o erro lógico geral de verificar o estado do fluxo antes de tentar ler dele, o que significa que para N itens de entrada o loop será executado N + 1 vezes, leva aos seguintes sintomas:
Se o fluxo estiver vazio, o loop será executado uma vez.
>>
falhará (não há entrada a ser lida) e todas as variáveis que deveriam ser definidas (porstream >> x
) são realmente não inicializadas. Isso leva ao processamento de dados de lixo, que podem se manifestar como resultados sem sentido (geralmente grandes números).(Se a sua biblioteca padrão estiver em conformidade com o C ++ 11, as coisas estão um pouco diferentes agora: agora, uma falha
>>
define as variáveis numéricas em0
vez de deixá-las não inicializadas (excetochar
s).)Se o fluxo não estiver vazio, o loop será executado novamente após a última entrada válida. Como na última iteração todas as
>>
operações falham, é provável que as variáveis mantenham seu valor da iteração anterior. Isso pode se manifestar como "a última linha é impressa duas vezes" ou "o último registro de entrada é processado duas vezes".(Isso deve se manifestar um pouco diferente desde C ++ 11 (veja acima): Agora você obtém um "registro fantasma" de zeros em vez de uma última linha repetida.)
Se o fluxo contiver dados malformados, mas você apenas verificar
.eof
, você terá um loop infinito.>>
falhará ao extrair quaisquer dados do fluxo; portanto, o loop gira no lugar sem nunca chegar ao fim.Para recapitular: A solução é testar o sucesso da
>>
operação em si, para não usar um separar.eof()
método:while (stream >> n >> m) { ... }
, tal como em C você testar o sucesso dascanf
própria chamada:while (scanf("%d%d", &n, &m) == 2) { ... }
.fonte