[c++] ¿Por qué iostream :: eof está dentro de una condición de bucle considerada incorrecta?



Answers

Conclusión: con un manejo adecuado del espacio en blanco, se puede usar eof (e incluso, ser más confiable que fail() para la comprobación de errores):

while( !(in>>std::ws).eof() ) {  
   int data;
   in >> data;
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}    

( Agradece a Tony D la sugerencia de resaltar la respuesta. Consulte su comentario a continuación para ver un ejemplo de por qué esto es más sólido ) .

El principal argumento en contra del uso de eof() parece faltar una sutileza importante sobre el papel del espacio en blanco. Mi proposición es que comprobar eof() explícitamente no solo no es " siempre incorrecto ", lo que parece ser una opinión predominante en este y otros hilos de SO similares, sino que con un manejo adecuado del espacio en blanco, proporciona un limpiador y un manejo de errores más confiable, y es la solución siempre correcta (aunque no necesariamente la más estricta).

Para resumir lo que se sugiere como la terminación "adecuada" y el orden de lectura es la siguiente:

int data;
while(in >> data) {  /* ... */ }

// which is equivalent to 
while( !(in >> data).fail() )  {  /* ... */ }

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 existe una manera fácil de distinguir entre una secuencia exitosa y otra que realmente falla por otros motivos distintos de eof. Toma las siguientes transmisiones:

  • 1 2 3 4 5<eof>
  • 1 2 a 3 4 5<eof>
  • a<eof>

while(in>>data) termina con un set failbit para las tres entradas. En el primero y el tercero, también se establece eofbit . Entonces, más allá del ciclo, se necesita una lógica extra muy fea para distinguir una entrada adecuada (1ra) de las incorrectas (2da y 3ra).

Considerando que, tome lo siguiente:

while( !in.eof() ) 
{  
   int data;
   in >> data;
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}    

Aquí, in.fail() verifica que mientras haya algo para leer, es el correcto. Su propósito no es un mero while-loop terminator.

Hasta ahora todo va bien, pero ¿qué ocurre si hay un espacio en la secuencia - lo que parece ser la principal preocupación contra eof() como terminator?

No necesitamos renunciar a nuestro manejo de errores; acaba de comer el espacio en blanco:

while( !in.eof() ) 
{  
   int data;
   in >> data >> ws; // eat whitespace with std::ws
   if ( in.fail() ) /* handle with break or throw */; 
   // now use data
}

std::ws omite cualquier espacio potencial (cero o más) en la secuencia mientras configura el eofbit , y no el failbit . Por lo tanto, in.fail() funciona como se espera, siempre que haya al menos un dato para leer. Si las transmisiones en blanco también son aceptables, entonces la forma correcta es:

while( !(in>>ws).eof() ) 
{  
   int data;
   in >> data; 
   if ( in.fail() ) /* handle with break or throw */; 
   /* this will never fire if the eof is reached cleanly */
   // now use data
}

Resumen: una construcción adecuada while(!eof) no solo es posible y no está mal, sino que permite que los datos se encuentren dentro del alcance, y proporciona una separación más clara de la comprobación de errores de la actividad habitual. Dicho esto, while(!fail) es indiscutiblemente una expresión más común y concisa, y puede preferirse en escenarios simples (datos individuales por tipo de lectura).

Question

Acabo de encontrar un comentario en this respuesta que dice que el uso de iostream::eof en una condición de bucle es "casi seguro que está mal". Por lo general, uso algo como while(cin>>n) , que supongo que comprueba implícitamente EOF, ¿por qué se comprueba si eof utiliza iostream::eof wrong de forma iostream::eof ?

¿Cómo es diferente de usar scanf("...",...)!=EOF en C (que a menudo uso sin problemas)?




1 while (!read.fail()) {
2     cout << ch;
3     read.get(ch);
4 }

Si utiliza la línea 2 en 3 y la línea 3 en 2, obtendrá ch impresa dos veces. Entonces cout antes de leer.




Related