29/01/2013
Para cualquier desarrollador que trabaje con Delphi o Free Pascal, el manejo de archivos es una tarea fundamental y cotidiana. Sin embargo, esta interacción con el sistema de archivos no está exenta de problemas. Discos llenos, archivos que no existen, permisos denegados... son solo algunos de los escenarios que pueden provocar fallos en nuestras aplicaciones. Es aquí donde entra en juego una de las excepciones más importantes y comunes del ecosistema Pascal: EInOutError. Comprender a fondo esta excepción no es solo una buena práctica, es un requisito indispensable para construir software estable, confiable y profesional.
Esta guía completa desglosará todo lo que necesitas saber sobre EInOutError, desde su definición básica hasta las técnicas avanzadas para su manejo, pasando por la decodificación de sus códigos de error y la importancia crítica de la configuración del compilador. Si alguna vez te has encontrado con un error de entrada/salida y no has sabido cómo reaccionar, este artículo es para ti.
¿Qué es Exactamente EInOutError?
En términos simples, EInOutError es la clase de excepción que el sistema de ejecución (runtime) de Delphi y Free Pascal lanza cuando se produce un error durante una operación de entrada/salida (E/S) de archivos. Piensa en ella como el guardián que vigila todas las interacciones de tu programa con los archivos en el disco.
Sin embargo, hay una condición crucial para que esta excepción sea lanzada: la comprobación de E/S (conocida en inglés como I/O checking) debe estar activada. Esta es una directiva del compilador que instruye al programa para que, después de cada operación de archivo, verifique si tuvo éxito. Si la operación falla, se genera y lanza automáticamente una instancia de EInOutError, deteniendo el flujo normal del programa y buscando un bloque try...except que pueda manejarla.
La Pieza Clave: La Comprobación de E/S (`I/O checking`)
La directiva del compilador que controla este comportamiento es {$I}. Puedes usarla de dos maneras:
{$I+}o{$IOCHECKS ON}: Activa la comprobación de E/S. Este es el estado recomendado y, por lo general, el predeterminado para proyectos nuevos. Cuando está activa, cualquier error de archivo lanzará una excepción EInOutError.{$I-}o{$IOCHECKS OFF}: Desactiva la comprobación de E/S. En este modo, si una operación de archivo falla, el programa no se detendrá ni lanzará una excepción. En su lugar, el error se registra internamente y la ejecución continúa.
Cuando la comprobación está desactivada, recae sobre el programador la responsabilidad de verificar manualmente el resultado de cada operación. Esto se hace llamando a la función IOResult inmediatamente después de la operación de archivo. Esta función devuelve 0 si la operación fue exitosa, o un código de error numérico si falló. Es vital llamar a IOResult, ya que también limpia el estado de error interno, permitiendo que las siguientes operaciones de E/S se ejecuten.
Aunque desactivar la comprobación puede parecer útil para un control manual más granular, es una práctica propensa a errores. Es muy fácil olvidar una verificación de IOResult, lo que puede llevar a comportamientos inesperados y fallos difíciles de depurar en otras partes del código. La recomendación moderna es casi unánime: mantén siempre la comprobación de E/S activada ({$I+}) y maneja los errores utilizando bloques try...except. Este enfoque es más limpio, más seguro y consistente con el manejo de otras excepciones en tu aplicación.
Ejemplo Práctico
Veamos un código simple que intenta abrir un archivo que no existe. Con la comprobación de E/S activada, el resultado es predecible.
program EjemploIO; {$APPTYPE CONSOLE} {$IOCHECKS ON} // Nos aseguramos de que la comprobación esté activa uses SysUtils; var F: TextFile; nombreArchivo: string; begin nombreArchivo := 'C:\ruta\inexistente\archivo.txt'; AssignFile(F, nombreArchivo); try // Esta línea intentará abrir un archivo que no existe // y lanzará una excepción EInOutError Reset(F); Writeln('El archivo se abrió correctamente.'); // Esta línea nunca se ejecutará CloseFile(F); except on E: EInOutError do begin Writeln('Ocurrió un error de E/S al intentar abrir el archivo.'); Writeln('Mensaje de la excepción: ', E.Message); Writeln('Código de error del sistema: ', E.ErrorCode); end; end; Readln; end.Decodificando la Excepción: La Propiedad `ErrorCode`
Cuando capturas una excepción EInOutError, el objeto de la excepción contiene una propiedad extremadamente útil: ErrorCode. Este miembro de tipo Integer te proporciona el código numérico específico del error que ocurrió.
Los códigos de error se dividen en dos rangos principales:
- 0 a 99: Representan errores nativos del sistema operativo (SO). Estos son códigos de error genéricos que no son exclusivos de Delphi. Para obtener una descripción textual de estos errores, puedes utilizar la función
SysErrorMessage(ErrorCode). - 100 en adelante: Son códigos de error definidos específicamente por el sistema de ejecución de Delphi para condiciones de error de E/S comunes y de alto nivel.
Tabla de Códigos de Error Comunes de Delphi
A continuación, se presenta una tabla con los errores de E/S más frecuentes definidos por Delphi, que te encontrarás a menudo en tu día a día como desarrollador.
| Código | Nombre (Referencia en Inglés) | Descripción Detallada |
|---|---|---|
| 100 | Disk read error | Ocurre al usar Read en un archivo tipado si se intenta leer más allá del final del archivo (EOF). |
| 101 | Disk write error | Reportado por CloseFile, Write, Writeln, o Flush si el disco se llena durante la operación. |
| 102 | File not assigned | Se produce si se llama a Reset, Rewrite, Append, Rename, o Erase sobre una variable de archivo a la que no se le ha asignado un nombre con AssignFile. |
| 103 | File not open | Lanzado por CloseFile, Read, Write, Seek, y otras funciones si el archivo no está actualmente abierto. |
| 104 | File not open for input | Ocurre al intentar leer (Read, Readln, etc.) de un archivo de texto que no fue abierto en modo de lectura con Reset. |
| 105 | File not open for output | Se produce al intentar escribir (Write, Writeln) en un archivo de texto que no fue abierto en modo de escritura (con Rewrite o Append). Es un error común en aplicaciones GUI. |
| 106 | Invalid numeric format | Reportado por Read o Readln al leer de un archivo de texto si el dato leído no corresponde con el formato numérico esperado (por ejemplo, leer 'abc' en una variable de tipo Integer). |
Caso Especial: Error 105 en Aplicaciones GUI
Un escenario particularmente común para el error 105 ("File not open for output") ocurre cuando los desarrolladores intentan usar procedimientos de consola como Writeln o Write para depurar dentro de una aplicación de interfaz gráfica de usuario (GUI). Por defecto, una aplicación GUI no tiene una consola de salida estándar asignada, por lo que estas llamadas fallan.
Para solucionar esto y poder usar la E/S de consola en una aplicación VCL o FMX, es necesario asignar explícitamente una consola. Esto se logra con las llamadas a la API de Windows AllocConsole y FreeConsole.
uses Winapi.Windows; // Necesario para AllocConsole y FreeConsole procedure TForm1.Button1Click(Sender: TObject); begin // Asignamos una consola a nuestra aplicación GUI AllocConsole; try // Ahora podemos usar Writeln sin problemas Writeln('Este es un mensaje de depuración en la consola.'); Writeln('El valor de X es: ', MiVariableX); Readln; // Espera a que el usuario presione Enter finally // Es crucial liberar la consola cuando ya no se necesita FreeConsole; end; end;Preguntas Frecuentes (FAQ)
¿Por qué mi programa no lanza una EInOutError si sé que hay un error de archivo?
La causa más probable es que la comprobación de E/S esté desactivada en esa parte de tu código (con {$I-}) o globalmente en las opciones del proyecto. En este caso, el programa no se detiene y debes verificar el resultado manualmente llamando a la función IOResult después de la operación fallida.
¿Cómo puedo obtener un mensaje de error más descriptivo que solo un número?
Dentro de un bloque except on E: EInOutError do, puedes usar la propiedad E.Message para obtener la descripción genérica. Para los códigos de error del sistema operativo (ErrorCode < 100), la función SysErrorMessage(E.ErrorCode) te dará una descripción mucho más detallada y específica del problema.
¿Es mejor usar `{$I-}` y `IOResult` o manejar excepciones?
Para el desarrollo de aplicaciones modernas, el manejo de excepciones con try...except y {$I+} es el método preferido. Es más robusto, menos propenso a errores humanos (como olvidar una verificación de IOResult) y mantiene el código más limpio y legible. El uso de IOResult es una herencia de los tiempos de Turbo Pascal y generalmente se desaconseja a menos que tengas una razón muy específica y controlada para ello.
¿Qué significa el error 103, "File not open"?
Este error indica que estás intentando realizar una operación (como leer, escribir, buscar una posición o cerrar) en una variable de archivo que no ha sido abierta con éxito previamente mediante una llamada a Reset (para lectura), Rewrite (para escritura desde cero) o Append (para añadir al final).
Si quieres conocer otros artículos parecidos a EInOutError en Delphi: Guía Esencial de Errores puedes visitar la categoría Juegos.
