5.3. Manejo errores de stream

El extractor de Date mostrado antes activa el bit de error de un stream bajo ciertas condiciones. ¿Como sabe un usuario que este error ha ocurrido? Puede detectar errores del stream llamando a ciertas funciones miembro del stream para ver si tenemos un estado de error, o si a usted no le preocupa qué tipo de error ha pasado, puede evaluar el stream en un contexto Booleano. Ambas técnicas derivan del estado del bit de error de un stream.

5.3.1. Estados del stream

La clase ios_base, desde la que ios deriva,[12]define cuatro banderas que puede usar para comprobar el estado de un stream:

Bandera

Significado

badbit

Algún error fatal (quizás físico) ha ocurrido. El stream debe considerarse no usable.

eofbit

Ha ocurrido un final de entrada (ya sea por haber encontrado un final físico de un stream de fichero o por que el usuario ha terminado el stream de consola, (usando un Ctrl-Z o Ctrl-D).

failbit

Una operación de E/S ha fallado, casi seguro que por datos inválidos (p.e. encontrar letras cuando se intentaba leer un número). El stream todavía se puede usar. El failbit también se activa cuando ocurre un final de entrada.

goodbit

Todo va bien; no hay errores. La final de la entrada todavía no ha ocurrido.

Puede comprobar si alguna de estas condiciones ha ocurrido llamando a la función miembro correspondiente que retorna un valor Booleano indicando cual de estas ha sido activada. La función miembro de stream good() retorna cierto si ninguno de los otros tres bits se han activado. La función eof() retorna cierto si eofbit está activado, que ocurre con un intento de leer de un stream que ya no tiene datos (generalmente un fichero). Como el final de una entrada ocurre en C++ cuando tratamos de leer pasado el final del medio físico, failbit también se activa para indicar que los datos esperados no han sido correctamente leídos. La función fail() retorna cierto si failbit o badbit están activados, y bad() retorna cierto solo si badbit está activado.

Una vez alguno de los bit de error de un stream se activa, permanece activo, cosa que no siempre es lo que se quiere. Cuando leemos un fichero, usted puede querer colocarse en una posición anterior en el fichero antes de su final. Simplemenete moviendo el puntero del fichero no se desactiva el eofbit o el failbit; debe hacerlo usted mismo con la función clear(), haciendo algo así:

myStream.clear(); // Clears all error bits

Después de llamar a clear(), good() retornará cierto si es llamada inmediatamente. Como vió en el extractor de Date antes, la función setstate() activa los bits que usted le pasa.¿Eso significa que setstate no afecta a los otros bits? Si ya esta activo, permanece activo. Si usted quiere activar ciertos bits pero en el mismo momento, desactivar el resto, usted puede llamar una versión sobrecargada de clear(), pasandole una expresion binaria representando los bits que quiere que se activen, así:

myStream.clear(ios::failbit | ios::eofbit);

La mayoría del tiempo usted no estará interesado en comprobar los bits de estado del stream individualmente. Generalmente usted simplemente quiere conocer si todo va bien. Ese es el caso cuando quiere leer un fichero del principio al final; usted quiere saber simplemente cuando la entrada de datos se ha agotado. Puede usar una conversion de la función definida para void* que es automáticamente llamada cuando un stream esta en una expresión booleana. Leer un stream hasta el final de la entrada usando este idioma se parece a lo siguiente:

int i;
while(myStream >> i)
  cout << i << endl;

Recuerde que operator>>() retorna su argumento stream, así que la sentencia while anterior comprueba el stream como una expresión booleana. Este ejemplo particular asume que el stream de entrada myStream contiene enteros separados por un espacio en blanco. La función ios_base::operator void*() simplemente llama a good() en su stream y retorna el resultado.[13] Como la mayoría de operaciones de stream retornan su stream, usar ese idioma es conveniente.



[12] Por esa razón usted puede escribir ios::failbit en lugar de ios_base::failbit para ahorrar pulsaciones.

[13] Es común el uso de operator void*() en vez de operator bool() porque las conversiones implícitas de booleano a entero pueden causar sorpresas; pueden emplazarle incorrectamente un stream en un contexto donde una conversion a integer puede ser aplicada. La función operator void*() solo será llamada implícitamente en el cuerpo de una expresión booleana.