c++ fstream eof

c++ - ¿Por qué se establece failbit cuando se encuentra eof en la lectura?



fstream (3)

He leído que <fstream> es anterior a <exception> . Ignorando el hecho de que las excepciones en fstream no son muy informativas, tengo la siguiente pregunta:

Es posible habilitar excepciones en secuencias de archivos usando el método exceptions() .

ifstream stream; stream.exceptions(ifstream::failbit | ifstream::badbit); stream.open(filename.c_str(), ios::binary);

Cualquier intento de abrir un archivo inexistente, un archivo sin los permisos correctos o cualquier otro problema de E / S resultará en una excepción. Esto es muy bueno usando un estilo de programación asertivo. Se suponía que el archivo debería estar allí y ser legible. Si no se cumplen las condiciones, obtenemos una excepción. Si no estuviera seguro de si el archivo podía abrirse de forma segura, podría usar otras funciones para probarlo.

Pero ahora supongamos que intento leer en un búfer, como este:

char buffer[10]; stream.read(buffer, sizeof(buffer));

Si el flujo detecta el final del archivo antes de llenar el búfer, el flujo decide establecer el failbit y se dispara una excepción si estaban habilitados. ¿Por qué? ¿Cuál es el punto de esto? Pude haber verificado que solo eof() después de la lectura:

char buffer[10]; stream.read(buffer, sizeof(buffer)); if (stream.eof()) // or stream.gcount() != sizeof(buffer) // handle eof myself

Esta opción de diseño me impide usar excepciones estándar en las transmisiones y me obliga a crear mi propio manejo de excepciones en permisos o errores de E / S. ¿O me estoy perdiendo algo? ¿Hay alguna manera de salir? Por ejemplo, ¿puedo probar fácilmente si puedo leer bytes de sizeof(buffer) en el flujo antes de hacerlo?


El failbit está diseñado para permitir que la transmisión informe que alguna operación no se completó correctamente. Esto incluye errores como no abrir el archivo, intentar leer datos que no existen e intentar leer datos del tipo incorrecto.

El caso particular que estás preguntando se reimprime aquí:

char buffer[10]; stream.read(buffer, sizeof(buffer));

Su pregunta es por qué se establece failbit cuando se llega al final del archivo antes de que se lea toda la entrada. La razón es que esto significa que la operación de lectura falló, usted pidió leer 10 caracteres, pero no había suficientes caracteres en el archivo. En consecuencia, la operación no se completó con éxito, y las señales de flujo fallan para avisarle esto, a pesar de que se leerán los caracteres disponibles.

Si desea realizar una operación de lectura en la que desea leer hasta cierto número de caracteres, puede utilizar la función miembro de readsome :

char buffer[10]; streamsize numRead = stream.readsome(buffer, sizeof(buffer));

Esta función leerá los caracteres hasta el final del archivo, pero a diferencia de la read , no establece el bit de fallo si se llega al final del archivo antes de que se lean los caracteres. En otras palabras, dice "intenta leer tantos caracteres, pero no es un error si no puedes. Solo déjame saber cuánto lees". Esto contrasta con la read , que dice "Quiero precisamente este número de caracteres, y es un error si no puedes hacerlo".

EDITAR : un detalle importante que olvidé mencionar es que eofbit se puede configurar sin disparar failbit. Por ejemplo, supongamos que tengo un archivo de texto que contiene el texto

137

sin nuevas líneas o espacios en blanco al final después. Si escribo este código:

ifstream input("myfile.txt"); int value; input >> value;

Entonces, en este punto, input.eof() devolverá verdadero, porque al leer los caracteres del archivo, la secuencia llega al final del archivo para ver si hay otros caracteres en la secuencia. Sin embargo, input.fail() no devolverá verdadero, porque la operación se realizó input.fail() , de hecho podemos leer un entero desde el archivo.

¡Espero que esto ayude!


Mejorando la respuesta de @ absentismo, sigue un método readeof() que hace lo mismo de read() pero no establece failbit en EOF. También se han probado fallas de lectura reales, como una transferencia interrumpida por la extracción de una memoria USB o la caída de un enlace en un acceso compartido de red. Se ha probado en Windows 7 con VS2010 y VS2013 y en Linux con gcc 4.8.1. En linux solo se ha intentado retirar la memoria USB.

#include <iostream> #include <fstream> using namespace std; streamsize readeof(ifstream &stream, char* buffer, streamsize count) { // This consistently fails on gcc (linux) 4.8.1 with failbit set on read // failure. This apparently never fails on VS2010 and VS2013 (Windows 7) streamsize reads = stream.rdbuf()->sgetn(buffer, count); // This rarely sets failbit on VS2010 and VS2013 (Windows 7) on read // failure of the previous sgetn() stream.rdstate(); // On gcc (linux) 4.8.1 and VS2010/VS2013 (Windows 7) this consistently // sets eofbit when stream is EOF for the conseguences of sgetn(). It // should also throw if exceptions are set, or return on the contrary, // and previous rdstate() restored a failbit on Windows. On Windows most // of the times it sets eofbit even on real read failure stream.peek(); return reads; } #define BIGGER_BUFFER_SIZE 200000000 int main(int argc, char* argv[]) { ifstream stream; stream.exceptions(ifstream::badbit | ifstream::failbit); stream.open("<big file on usb stick>", ios::binary); char *buffer = new char[BIGGER_BUFFER_SIZE]; streamsize reads = readeof(stream, buffer, BIGGER_BUFFER_SIZE); if (stream.eof()) cout << "eof" << endl << flush; delete buffer; return 0; }

En pocas palabras: en linux el comportamiento es más consistente y significativo. Con las excepciones habilitadas en fallas de lectura reales, se lanzará en sgetn() . Por el contrario, Windows tratará las fallas de lectura como EOF la mayoría de las veces.


Usar el búfer subyacente directamente parece hacer el truco:

char buffer[10]; streamsize num_read = stream.rdbuf()->sgetn(buffer, sizeof(buffer));