TEMA 1.17
CONTROL DE ERRORES





1.17.1 - INSTRUCCIÓN GOTO.
LA MÁS CONOCIDA Y ODIADA DE BASIC

Empecemos con un ejemplo "muy malo":

	CLS
Pricipio:
	PRINT "Esto es el principio"
	GOTO Final
Enmedio:
	PRINT "Esto es lo de en medio, pero no lo verás"
Final:
	PRINT "Esto es el Final"

Que daría como resultado en pantalla:

Esto es el principio
Esto es el final

Ahora veamos como funciona:

En este programa hemos definido tres etiquetas. Para definir etiquetas usamos las mismas normas que para los nombres de variables y al final le colocamos el carácter dos puntos (:). Las etiquetas siempre van al principio de la línea y si usamos etiquetas es útil encolumnar todas las demás instrucciones más a la derecha, para verlas al vuelo.

Podemos definir etiquetas en cualquier módulo de nuestro programa, pero QBasic solo las "encontrará" si están definidas en el módulo principal. Por esto no tiene sentido definir etiquetas dentro de procedimientos y funciones.

Las etiquetas no hacen nada, simplemente están ahí. Únicamente nos sirven para poder "Saltar" a ellas usando instrucciones especiales como la famosa GOTO (Go to..., ir a...) que salta incondicional e inmediatamente a la etiqueta que le indiquemos para seguir desde allí con la ejecución del programa.

Sencillo ¿No?. En verdad es muy sencillo pero puede dar lugar a toda una variedad de problemas lógicos que pueden ser casi imposibles de detectar, aislar y solucionar. Imagina un programa donde en vez de tres etiquetas haya trescientas y entre ellas haya centenares de instrucciones GOTO que dirigen la ejecución del programa a un sitio lejano de nuestro listado. Una mala planificación del programa seguramente hará que bloques enteros no se lleguen a ejecutar nunca o que se produzcan bucles infinitos que lleguen a bloquear el programa. Por eso a los programas que usan estas instrucciones se les conoce como "Código Espagueti".

En los lenguajes de bajo nivel como es el caso de los ensambladores y en lenguajes de guiones muy sencillos como el que se utiliza para construir los ficheros .BAT de MS-DOS se usan intensivamente las etiquetas y las instrucciones de bifurcación o salto incondicional, pero los modernos lenguajes de programación estructurada (QBasic lo es) nos dan la posibilidad de usar estructuras de control iterativas (Bucles PARA, MIENTRAS y REPETIR), así como los procedimientos y las funciones que nos permiten en todo caso saber como funciona nuestro programa para poder solucionar posibles errores. Por esto es muy recomendable EVITAR A TODA COSTA EL USO DE ESTA METODOLOGÍA DE PROGRAMACIÓN CON INSTRUCCIONES GOTO Y SIMILARES en los lenguajes de alto nivel, salvo en el caso de las instrucciones de manejo de errores que vamos a ver en los siguientes apartados y que son el motivo de esta explicación.





1.17.2 - INTRODUCCIÓN A LOS ERRORES

Hemos ido viendo a lo largo de los temas anteriores que hay tres tipos de errores:

Los primeros suelen ser cosas mal escritas o que no cumplen con las normas de sintaxis del lenguaje de programación. La mayoría de ellos en QBasic serán mostrados conforme vamos escribiendo nuestro código al pasar de línea, o en todo caso al intentar ejecutar el programa. Tendremos que corregirlos modificando lo que este mal para poder iniciar el programa.

Los errores de tiempo de ejecución se producen durante la ejecución del programa y son provocados por intentar hacer algo que no está permitido, como dividir entre cero, o bien porque alguno de los dispositivos del ordenador falle (Memoria agotada, no se encuentra un fichero, la impresora no tiene papel, etc...). Estos errores hacen que el programa se detenga. En QBasic se volverá al editor y se mostrará un recuadro con el mensaje apropiado. En otros lenguajes o en programas ya compilados puede salir desde un simple mensaje hasta una pantalla azul típica de Windows o lo más normal es que el ordenador deje de responder y haya que reiniciarlo.

Los errores lógicos se deben a una mala planificación de los algoritmos, lo que da lugar, en el mejor de los casos, a que el programa funcione correctamente pero no haga lo que queremos y no resuelva el problema para el que ha sido diseñado, o lo más normal es que llegue a provocar un error de tiempo de ejecución porque se llene el espacio disponible en memoria o se entre en un bucle infinito.

En este tema vamos a ver cómo conseguir que cuando se produzca un error de tiempo de ejecución el programa no quede detenido inmediatamente, sino que se salga de él de una forma un poco más "elegante" o incluso se pueda solucionar el problema y seguir normalmente con la ejecución del programa.

Usando las técnicas de programación estructurada que hemos visto en este curso hasta antes del tema de ficheros si hacemos los programas bien NO TIENEN PORQUÉ PRODUCIRSE ERRORES DE TIEMPO DE EJECUCIÓN. Nosotros somos los responsables de evitar que los bucles sean infinitos, de no hacer referencias a índices de arrays que no existen, de que nada se llegue a dividir entre cero, etc... La única fuente potencial de errores son los datos que pedimos al usuario, pero si depuramos los datos de entrada de forma que no se acepten valores no permitidos como vimos en el tema de bucles podemos asegurar en la amplia mayoría de los casos que nuestro programa va a funcionar bien siempre, ya que solo acepta datos válidos.

El problema llega cuando empezamos a trabajar con dispositivos externos como son las unidades de disco donde se guardan los ficheros. Por muy bien hecho que esté nuestro programa no podemos evitar que el usuario quiera abrir un fichero que no existe, o de un disquete que todavía no ha introducido en la unidad correspondiente, o que se ha estropeado, que quiera escribir en un CD-ROM, en un disco que no tiene espacio suficiente o que está protegido contra escrituea. Sólo en este caso de los ficheros y en otros también especiales como cuando veamos cómo usar las impresoras será necesario y recomendable programar rutinas de manejo de errores de las que vamos a ver en el siguiente apartado. En otros casos no nos merece la pena usar esto ya que las instrucciones GOTO ofrecen una forma especialmente mala de estructurar los programas y podemos despreciar la posibilidad de que se produzca algún error raro como que se agote la memoria por culpa de otro programa y el nuestro no pueda seguir funcionando.





1.17.3 - CONTROL DE ERRORES

Para activar el control de errores en QBasic usaremos la siguiente instrucción:

ON ERROR GOTO linea

Dónde linea es el nombre de una etiqueta que se haya definido en el programa principal.
Esto quiere decir "Cuando se produzca un error ve inmediatamente a la etiqueta que se llama linea y sigue desde allí".
De esta forma conseguimos que el programa no se detenga inmediatamente al producirse un error.

Como hemos dicho antes, solo debemos controlar los errores en instrucciones peligrosas como es el caso de las que abren los ficheros (OPEN). Una ver pasadas estas instrucciones debemos desactivar el manejo de errores usando la instrucción:

ON ERROR GOTO 0

Lo último de esta instrucción es un cero. A partir de ahora cuando se produzca un error ya no habrá manejo de errores y el programa se detendrá, pero si lo hemos hecho bien no tienen porqué producirse errores.

Lo normal es activar el manejo de errores al principio de un procedimiento que, por ejemplo, abra un fichero y desactivarlo al final, antes de salir. De esta forma tenemos claramente delimitado donde usamos el manejo de errores, que estará activo solo mientras se ejecute ese módulo, y no en el resto del programa que no usa instrucciones "conflictivas".

Vamos con un ejemplo completo:

'Programa principal
CLS
INPUT "Nombre del fichero: ", fich$
MuestraTamanno fich$

END

manejaerrores:

SELECT CASE ERR
   CASE 53
      PRINT "No se ha encontrado el archivo"
   CASE 68
      PRINT "No se ha encontrado la unidad de disco"
   CASE 71
      PRINT "No se ha encontrado el disco en la unidad"
   CASE 76
      PRINT "No se ha encontrado el directorio"
   CASE ELSE
      PRINT "Se ha producido el error"; ERR
END SELECT

END

'Este procedimiento abre el archivo, muestra el tamaño y cierra
'Entrada: f$: El nombre del archivo
SUB MuestraTamanno (f$)
   ON ERROR GOTO manejaerrores
   OPEN f$ FOR INPUT AS #1
   PRINT "El fichero ocupa"; LOF(1); "bytes."
   CLOSE
   ON ERROR GOTO 0
END SUB

Este programa pide al usuario que escriba el nombre de un fichero para abrirlo y mostrar su tamaño usando la función LOF que ya conocemos.

Si escribimos un nombre de archivo válido no se activa para nada el manejo de errores y el programa funciona normalmente, nos calcula el tamaño en bytes del archivo y termina al llegar a la instrucción END que hay justo después de la llamada al procedimiento. El resultado podría ser:

Nombre del fichero: c:\autoexec.bat
El fichero ocupa 219 bytes.

Veamos como funciona el programa paso a paso en caso de que el fichero exista y no se produzca el error:

Aquí encontramos como novedad la instrucción END que hace que el programa termine de forma normal aunque no hayamos llegado al final del listado. La tendremos que poner siempre en el módulo principal antes de la etiqueta del manejador de errores para evitar que estas instrucciones se ejecuten en caso normal de que no haya un error. En otros casos no es recomendable usar esta instrucción, los programas y módulos deben empezar por el principio y terminar por el final del listado.

Ahora vamos a ver como funcionaría el programa paso a paso en el caso de que quisiéramos abrir un fichero que no existe y se produjera el error.

Hay que tener claro que al producirse el error el fichero no se ha abierto, y por lo tanto el descriptor #1 no contiene referencia a ningún fichero, por lo que no podemos intentar hacer nada con él, ni siquiera cerrar el fichero.

Hay un gran número de códigos de error, puedes ver una lista completa de ellos en la ayuda en pantalla de Qbasic. Nunca es necesario controlarlos todos, por ejemplo si estamos trabajando con ficheros solo pondremos algunos de los relacionados con ficheros. Siempre es conveniente usar algo como CASE ELSE para que quede controlado alguno que se nos haya podido escapar, en este caso bastará con mostrar el número por pantalla para poder identificar el error de alguna forma, pero siempre haciendo que el programa termine bien.

Se pueden hacer manejos de errores mucho más elaborados que lleguen a solucionar el problema y el programa pueda seguir funcionando como si nada. Imagina que queremos abrir un fichero .INI dónde está la configuración del programa, y este se ha borrado. En este caso de forma totalmente transparente al usuario podríamos crear uno nuevo vacío o con valores por defecto y dejarlo abierto para que el programa siga trabajando con él normalmente. En este caso usaríamos la instrucción RESUME para continuar con el programa por la línea siguiente a la que produjo el error, una vez que ya hayamos abierto el nuevo fichero para escritura, hayamos escrito en él lo que sea, lo hayamos cerrado y lo hayamos vuelto a abrir para lectura, todo esto en el manejador de errores.













CuRSo De iNTRoDuCCióN a La PRoGRaMaCióN CoN QBaSiC
© 2004 Juan M. González