Acabo de encontrar un comentario en esta respuesta que dice que usar iostream::eof
en una condición de bucle es "casi seguro que está mal". Generalmente uso algo como while(cin>>n)
, que supongo que comprueba implícitamente EOF.
¿Por qué es while (!cin.eof())
incorrecto verificar eof explícitamente ?
¿Cómo es diferente de usar scanf("...",...)!=EOF
en C (que a menudo uso sin problemas)?
scanf(...) != EOF
tampoco funcionará en C, porquescanf
devuelve el número de campos analizados y asignados correctamente. La condición correcta esscanf(...) < n
dónden
está el número de campos en la cadena de formato.EOF
si se encuentra el final del archivo antes de la primera conversión de campo (exitosa o no). Si se alcanza el final del archivo entre los campos, devolverá el número de campos convertidos y almacenados con éxito. Lo que hace la comparación con elEOF
mal..eof()
después de que sale el bucle.while(fail)
ciclo termina con un fallo real y un eof. Piense si necesita 3 ints por iteración (digamos que está leyendo un punto xyz o algo así), pero, erróneamente, solo hay dos ints en la secuencia.Respuestas:
Porque
iostream::eof
solo regresarátrue
después de leer el final de la transmisión. No , no indican, que la siguiente lectura será el final de la secuencia.Considere esto (y suponga que la próxima lectura será al final de la secuencia):
Contra esto:
Y en su segunda pregunta: porque
es lo mismo que
y no es lo mismo que
fuente
int
sostd::string
s o similar, el bit EOF se establece cuando extrae el justo antes del final y la extracción llega al final. No necesita leer de nuevo. La razón por la que no se configura al leer archivos es porque hay un extra\n
al final. He cubierto esto en otra respuesta . Leerchar
s es un asunto diferente porque solo extrae uno a la vez y no continúa hasta el final.while (!eof())
no "funcionará" enint
s ostd::string
s cuando la entrada está totalmente vacía, por lo que incluso sabiendo que no\n
se necesita cuidado adicional."Hello"
(sin espacios en blanco o\n
) ystd::string
se extrae, extraerá las letras deH
ao
, dejará de extraer y entonces no establezca el bit EOF. De hecho, establecería el bit EOF porque fue el EOF el que detuvo la extracción. Solo esperando aclarar eso para la gente.Parte superior de la línea inferior: con el manejo adecuado del espacio en blanco, lo siguiente es cómo
eof
se puede usar (e incluso, ser más confiable quefail()
para la verificación de errores):( Gracias Tony D por la sugerencia de resaltar la respuesta. Vea su comentario a continuación para ver un ejemplo de por qué esto es más robusto ) .
Al argumento principal en contra del uso
eof()
parece faltarle una sutileza importante sobre el papel del espacio en blanco. Mi propuesta es que, verificareof()
explícitamente no solo no es " siempre incorrecto ", lo que parece ser una opinión primordial en este y otros hilos SO similares, sino que, con un manejo adecuado del espacio en blanco, proporciona un espacio más limpio y más confiable manejo de errores, y es la solución siempre correcta (aunque, no necesariamente la más estricta).Para resumir lo que se sugiere como la terminación "correcta" y el orden de lectura es el siguiente:
La falla debida al intento de lectura más allá de eof se toma como la condición de terminación. Esto significa que no hay una manera fácil de distinguir entre una transmisión exitosa y una que realmente falla por otras razones que no sean eof. Tome las siguientes transmisiones:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
termina con un conjuntofailbit
para las tres entradas. En el primero y tercero,eofbit
también se establece. Entonces, más allá del bucle, uno necesita una lógica extra muy fea para distinguir una entrada adecuada (primera) de las incorrectas (segunda y tercera).Mientras que, tome lo siguiente:
Aquí,
in.fail()
verifica que mientras haya algo para leer, es el correcto. Su propósito no es un simple terminador while-loop.Hasta ahora todo bien, pero ¿qué sucede si hay un espacio final en la secuencia? ¿Cuál parece ser la principal preocupación contra el
eof()
terminador?No necesitamos entregar nuestro manejo de errores; solo come el espacio en blanco:
std::ws
omite cualquier espacio final potencial (cero o más) en la secuencia al configurareofbit
y no elfailbit
. Por lo tanto,in.fail()
funciona según lo esperado, siempre que haya al menos un dato para leer. Si las secuencias en blanco también son aceptables, entonces la forma correcta es:Resumen: Una construcción adecuada
while(!eof)
no solo es posible y no está mal, sino que permite que los datos se localicen dentro del alcance y proporciona una separación más clara de la verificación de errores de la empresa como de costumbre. Dicho esto,while(!fail)
es indiscutiblemente un lenguaje más común y conciso, y puede preferirse en escenarios simples (datos únicos por tipo de lectura).fuente
eofbit
yfailbit
se establecen, en el otro sólofailbit
se establece. Solo necesita probar eso una vez que el ciclo ha terminado, no en cada iteración; solo dejará el bucle una vez, por lo que solo debe verificar por qué lo dejó una vez.while (in >> data)
funciona bien para todas las transmisiones en blanco.!eof & fail
bucle pasado. Hay casos en los que uno no puede confiar en esto. Ver comentario anterior ( goo.gl/9mXYX ). De todos modos, no estoy proponiendoeof
-check como la alternativa siempre mejor . Solo digo que es una forma posible y (en algunos casos más apropiada) de hacer esto, en lugar de "¡ciertamente equivocado!" como tiende a ser reclamado por aquí en SO.stream >> my_int
donde la secuencia contiene, por ejemplo, "-":eofbit
yfailbit
son conjunto. Eso es peor que eloperator>>
escenario, donde la sobrecarga proporcionada por el usuario al menos tiene la opción de borrareofbit
antes de regresar para ayudar awhile (s >> x)
usar el soporte . En términos más generales, esta respuesta podría usar una limpieza: solo el finalwhile( !(in>>ws).eof() )
es generalmente robusto y está enterrado al final.Porque si los programadores no escriben
while(stream >> n)
, posiblemente escriban esto:Aquí el problema es que no puede hacerlo
some work on n
sin verificar primero si la lectura de la transmisión fue exitosa, porque si no tuvo éxito,some work on n
produciría un resultado no deseado.El punto es que,
eofbit
,badbit
, ofailbit
se establece después de que se hizo un intento de leer de la corriente. Entonces, sistream >> n
falla, entonceseofbit
,badbit
ofailbit
se establece de inmediato, por lo que es más idiomático si escribewhile (stream >> n)
, porque el objeto devuelto sestream
convierte enfalse
si hubo algún error en la lectura de la secuencia y, en consecuencia, el ciclo se detiene. Y se convierte entrue
si la lectura fue exitosa y el ciclo continúa.fuente
n
, el programa también puede caer en un bucle infinito , si la operación de flujo fallido no consume ninguna entrada.Las otras respuestas han explicado por qué la lógica es incorrecta
while (!stream.eof())
y cómo solucionarlo. Quiero centrarme en algo diferente:En términos generales, verificar
eof
solo es incorrecto porque la extracción de flujo (>>
) puede fallar sin llegar al final del archivo. Si tiene, por ejemplo,int n; cin >> n;
y la secuencia contienehello
, entoncesh
no es un dígito válido, por lo que la extracción fallará sin llegar al final de la entrada.Este problema, combinado con el error lógico general de verificar el estado del flujo antes de intentar leerlo, lo que significa que para N elementos de entrada el ciclo se ejecutará N + 1 veces, conduce a los siguientes síntomas:
Si la secuencia está vacía, el ciclo se ejecutará una vez.
>>
fallará (no hay entrada para leer) y todas las variables que se suponía que debían establecerse (porstream >> x
) en realidad no se inicializan. Esto lleva a que se procesen datos basura, que pueden manifestarse como resultados sin sentido (a menudo, cantidades enormes).(Si su biblioteca estándar se ajusta a C ++ 11, las cosas son un poco diferentes ahora: un error
>>
ahora establece variables numéricas en0
lugar de dejarlas sin inicializar (excepto parachar
s)).Si la secuencia no está vacía, el ciclo se ejecutará nuevamente después de la última entrada válida. Dado que en la última iteración todas las
>>
operaciones fallan, es probable que las variables conserven su valor de la iteración anterior. Esto puede manifestarse como "la última línea se imprime dos veces" o "el último registro de entrada se procesa dos veces".(Esto debería manifestarse un poco diferente desde C ++ 11 (ver arriba): ahora obtienes un "registro fantasma" de ceros en lugar de una última línea repetida).
Si la secuencia contiene datos mal formados pero solo los verifica
.eof
, termina con un bucle infinito.>>
no podrá extraer ningún dato de la transmisión, por lo que el bucle gira en su lugar sin llegar al final.En resumen: La solución es poner a prueba el éxito de la
>>
operación en sí, no usar una separada.eof()
método:while (stream >> n >> m) { ... }
al igual que en C se prueba el éxito de lascanf
misma llamada:while (scanf("%d%d", &n, &m) == 2) { ... }
.fuente