23/11/10

El paquete TEXT_IO

El paquete TEXT_IO permite a Oracle Forms, ya que es un paquet interno de Oracle Forms (y a cualquier otro basado en el sistema CDE) realizar operaciones de lectura y escritura en los archivos del sistema operativo.

El paquete TEXT_IO se encuentra del lado del cliente y no puede tener acceso a los archivos del lado del servidor (a menos que la configuración de la red de ficheros lo permita).


1. Escribir en un fichero

Para escribir en un archivo que (en la mayoría de los casos) es necesario seguir los siguentes pasos:

  1. Declarar un manejador de archivo. Este asa sirve como un puntero en el archivo de las próximas convocatorias a la TEXT_IO paquete para manipular el contenido de este archivo.
  2. Abra el archivo con una llamada a FOPEN. Puede abrir un archivo para sustituir o añadir texto.
  3. Escribir datos en el archivo usando los procedmimientos PUT, PUTF o PUT_LINE.
  4. Cerrar el archivo con el procedimiento FCLOSE.

2. Leer datos de un fichero

Para poder leer datos de un archivo, en la mayoría de los casos,siga los siguientes pasos :

  1. Declarar un manejador de archivo. Este asa sirve como un puntero en el archivo de las próximas convocatorias a la TEXT_IO.
  2. Declarar una cadena VARCHAR2 que recibirá la línea de datos del fichero.
  3. Abra el archivo con FOPEN en modo "READ".
  4. Utilice el procedimiento GET_LINE para leer los datos del archivo y almacenarlos en la cadena declarada anteriormente. Para leer todas las líneas de un archivo, tendría que ejecutar GET_LINE en un bucle.
  5. Cerrar el archivo con una llamada a fclose.

3. Módulos y tipos del paquete TEXT_IO

El paquete TEXT_IO contiene todos los módulos y un tipo de dato especial para manejar los archivos crear archivos (FILE_TYPE).

Para llamar a cualquier módulo de este paquete se ha de poner anteriormente el nombre del paquete, "TEXT_IO"

3.1 FILE_TYPE

FILE_TYPE es en realidad un RECORD de PL/SQL definido por el paquete TEXT_IO. El es nuestro puntero al archivo y mantiene un registro de información sobre el archivo. se ha de pensar en FILE_TYPE como un tipo de dato especial para los archivos.

Para trabajar con un archivo de texto, primero se ha de declarar este puntero, o manejador, que sería de la siguiente forma:
DECLARE
  file_handle TEXT_IO.FILE_TYPE;
BEGIN
  ...
END;
Si se intenta hacer referencia a un manejador de archivo que no haya sido declarado o que actualmente apunta a null, se devuelve el error -30200 genérico de TEXT_IO. Ver el paquete TOOL_ERR

3.2 FOPEN

La función FOPEN abre el archivo especificado y devuelve un manejador de archivo que puede utilizar para manipular el archivo. La especificación de la función es:
FUNCTION fopen (file_name_in IN VARCHAR2, file_mode_in IN VARCHAR2) RETURN TEXT_IO.FILE_TYPE;

file_name_in es el nombre del archivo (incluida toda la información sobre la ruta necesaria para localizar el archivo) y file_mode_in es el modo en que el archivo se abrirá. Hay tres modos:

  • R - Abre el archivo sólo en modo lectura
  • W - Abre el archivo en modo escribir machacando todas las líneas existentes
  • A - Abre el archivo en modo añadir. Cuando se abre en modo append, todas las líneas existentes en el archivo se mantienen intactas. Nuevas líneas se añadirán después de la última línea file.

En el siguiente ejemplo muestra cómo declarar un manejador de archivo y abrir un archivo de configuración en el modo de sólo lectura:
DECLARE
  config_file TEXT_IO.FILE_TYPE;
BEGIN
  config_file := TEXT_IO.FOPEN ('config.txt', 'R');
  ...
END;

3.3 FCLOSE

Utilice FCLOSE para cerrar un fichero abierto. La especificación del procedimiento:
PROCEDURE FCLOSE (file_in TEXT_IO.FILE_TYPE);
donde file_in es el manejador del archivo.

En el siguiente procedimiento se cerrará el archivo pasado como argumento, después de utilizar la función IS_OPEN para asegurarse de que el archivo está abierto:
PROCEDURE close_file (file_in IN TEXT_IO.FILE_TYPE) IS
BEGIN
  IF TEXT_IO.IS_OPEN (file_in) THEN
    TEXT_IO.FCLOSE (file_in);
  END IF;
END;

3.4 GET_LINE

El procedimiento GET_LINE lee una línea de datos del archivo especificado, si este está abierto, siempre en la línea de buffer. La especificación del procedimiento es la siguiente:
PROCEDURE GET_LINE (file_in IN TEXT_IO.FILE_TYPE, line_out OUT VARCHAR2);
La variable especificada para el parámetro line_out debe ser lo suficientemente grande como para contener todos los datos hasta el próximo retorno de carro o hasta el EOF. Si no es así, PL/SQL lanzazará la excepción VALUE_ERROR.

Cuando GET_LINE intenta leer pasado el EOF no salta la excepción NO_DATA_FOUND.

3.5 IS_OPEN

La función IS_OPEN devuelve TRUE si el manejador especificado está actualmente abierto, en caso contrario devuelve FALSE. La especificación de esta función es:
FUNCTION IS_OPEN (file_in IN TEXT_IO.FILE_TYPE) RETURN BOOLEAN;
Un ejemplo de su uso es el procedimiento close_file, (este se encuentra en la descripción del procedimiento FCLOSE)

3.6 NEW_LINE

El procedimiento NEW_LINE inserta uno o más caracteres de nueva línea en el fichero especificado. La especificación para este procedimiento es:
PROCEDURE NEW_LINE (file_in IN TEXT_IO.FILE_TYPE, num_lines_in IN PLS_INTEGER := 1);
Donde num_lines_in es el número de lineas que va a ser insertado en el fichero. Sino se especifica este parámetro se usa el valor por defecto, o sea, uno, que pone una nueva línea (retorno de carro) al final de la línea actual. Por lo tanto, si desea insertar una línea en blanco en el archivo, se debe de ejecutar el siguiente llamamiento
TEXT_IO.NEW_LINE (my_file, 2);
Si se desea agregar con frecuencia un retorno de carro cada vez que se añadan datos al fichero, quizás sea aconsejable nuestra propia versión del procedimiento PUT (este procedimiento se analiza más adelante) de la siguiente manera:
PROCEDURE add_line (file_in IN TEXT_IO.FILE_TYPE, line_in IN VARCHAR2) IS
BEGIN
  TEXT_IO.PUT (file_in, line_in);
  TEXT_IO.NEW_LINE (file_in);
END;
Con el uso de add_line en lugar de PUT, no tendrá que preocuparse por recodar insertar un retorno de carro al final de la línea.

3.7 PUT

EL procedimiento PUT pone datos en el fichero especificado. Este procedimiento está muy sobrecargado con el fin de que se pueda llamar con diferente número de combinaciones de argumentos. Las especificaiones son:
PROCEDURE PUT (file_in TEXT_IO.FILE_TYPE, item_in IN VARCHAR2);
PROCEDURE PUT (item_in IN VARCHAR2);
PROCEDURE PUT (file_in TEXT_IO.FILE_TYPE, item_in IN DATE);
PROCEDURE PUT (item_in IN DATE);

PROCEDURE PUT (file_in TEXT_IO.FILE_TYPE, item_in IN NUMBER);
PROCEDURE PUT (item_in IN NUMBER);
PROCEDURE PUT (file_in TEXT_IO.FILE_TYPE, item_in IN PLS_INTEGER);
PROCEDURE PUT (item_in IN PLS_INTEGER);
Las dos formas básicas de PUT: una especifica el fichero de salida y la otra sólo el item de salida. Si no se proporciona el fichero de salida, PUT manda los datos al interprete de PL/SQL; durante la ejecución de un formulario las llamadas PUT en las que no se especifica el fichero son ignoradas.

El procedimiento PUT añade los datos a la línea actual en el archivo, no los inserta en una nueva línea. Para insertar en una nueva línea se ha de utilizar el procedimiento NEW_LINE.

3.8 PUTF

PUTF también pone escribe datos en un archivo, pero utiliza formato de mensaje (de ahí, la "F" en "PUTF") para interpretar los diferentes elementos que van a ser introducidos en el archivo. PUTF es un procedimiento que está sobrecargado para soportar de uno a cinco item diferentes de datos, así nos encontramos con:
PROCEDURE PUTF (file_in TEXT_IO.FILE_TYPE, item1_in IN VARCHAR2);
PROCEDURE PUTF
(file_in TEXT_IO.FILE_TYPE, format_in IN VARCHAR2, item1_in IN VARCHAR2);
PROCEDURE PUTF
(file_in TEXT_IO.FILE_TYPE, format_in IN VARCHAR2,
item1_in IN VARCHAR2, item2_in IN VARCHAR2);
PROCEDURE PUTF
(file_in TEXT_IO.FILE_TYPE, format_in IN VARCHAR2,
item1_in IN VARCHAR2, item2_in IN VARCHAR2, item3_in IN VARCHAR2);
PROCEDURE PUTF
(file_in TEXT_IO.FILE_TYPE, format_in IN VARCHAR2,
item1_in IN VARCHAR2, item2_in IN VARCHAR2, item3_in IN VARCHAR2,
item4_in IN VARCHAR2);
PROCEDURE PUTF
(file_in TEXT_IO.FILE_TYPE, format_in IN VARCHAR2,
item1_in IN VARCHAR2, item2_in IN VARCHAR2, item3_in IN VARCHAR2,
item4_in IN VARCHAR2, item5_in IN VARCHAR2);
Donde format_in es una cadena que le dice a PUTF cómo interpretar los diferentes datos a escribir. Además de "boilerplate" o texto literal, la cadena de formato puede contener los siguientes patrones:

  • %s: le indica a PUTF el item a poner en el archivo. Puede tener hasta cinco "% s" patrones en la cadena de formato, ya que PUTF tendrá un máximo de cinco parámetros
  • \n: le indica a PUTF poner un retorno de carro en el archivo. No hay límite al número de "\n"

También puede llamar sin PUTF un manejador de archivo, en cuyo caso la salida se dirige al intérprete de PL/SQL. Dado que este no juega un papel importante en Oracle Forms, lo voy a dejar ahí.

Observe que sólo PUTF toma VARCHAR2 cómo tipos de elementos. Si desea incluir una fecha o un número en una llamada a PUTF, antes se debe de pasar a VARCHAR2 con la función TO_CHAR. Además, si se desea escribir una sola cadena no se tiene que especificar una cadena de formato.

Ejemplo de la cadena de formato de PUTF
El siguiente ejemplo explica brevemente cómo se puede utilizar la cadena de formato.

Supongamos que se requiere que un archivo contenga lo siguiente:
Employee: Steven Feuerstein
Soc Sec #: 123-45-5678
Salary: $1000

Para ello con la siguiente llamada a PUTF bastaría:
TEXT_IO.PUTF
(file_handle, 'Employee: %s\nSoc Sec #: %s\nSalary: %s',
'Steven Feuerstein',
'123-45-5678',
TO_CHAR (:employee.salary, $9999));
Si se necesita escribir más de cinco item, simplemente llamamos PUTF dos veces consecutivas, o cuantas sea necesario, para alcanzar nuestro objetivo. Como se muestra a continuación:
TEXT_IO.PUTF
(file_handle, '%s\n%s\n%s\n%s\n%s\n',
TO_DATE (SYSDATE, 'MM/DD/YYYY'),
TO_CHAR (:pet.pet_id),
:pet.name,
TO_DATE (:pet.birth_date, 'MM/DD/YYYY'),
:pet.owner);
TEXT_IO.PUTF
(file_handle, '%s\n%s\n',
:pet.bites_mailperson,
:pet.does_tricks);

3.9 PUT_LINE

La tercera variante del PUT, dentro del paquete TEXT_IO, es PUT_LINE. Este procedimiento escribe datos en un fichero y además le añade un retorno de carro al final del texto escrito. La especificación de PUT_LINE es:
PROCEDURE PUT_LINE (file_in TEXT_IO.FILE_TYPE, item_in IN VARCHAR2);
Solamente podemos pasar a PUT_LINE una cadena de texto, por lo que si desea incluir una fecha o un número, primero se ha de utilizar la función TO_CHAR para convertir los datos.

Una llamada a PUT_LINE es quivalente a una llamada a PUT seguida de una llamada a NEW_LINE. Y todo esto a s vez equivale a llamar a PUTF siendo la cadena de formato '%s\n'.

4. Excepciones en el manejo de archivos de E/S

Cuando se intenta leer pasado el end of a file (EOF), PL/SQL nos devuelve la excepcion NO_DATA_FOUND. Todos los demás errores, incluyendo "file system full", "file not found" y el resto, lanzar la excepción genérica ORA-302000. Esta excepción se puede capturar con el manejador de excepciones WHEN OTHERS.

El error genérico -302000, cómo habrá observado, no es my útil. Si necesita saber la causa excata del error debe de utilizar el paquete TOOL_ERR. Ver el paquete TOOL_ERR

5. Operaciones estándar de E/S

En esta sección veremos las operaciones más comunes a realizar en un fichero. Lo más aconsejable es crearse estas, y las que se desarrollen, en un paquete, por ejemplo, UTILS_FILE.

5.1 Crear un archivo

La siguiente función crea un archivo de una línea. Si nos fiamos nos devuelve el manejador del archivo ya que el proposito de esta función es solamente crear el fichero.
PROCEDURE create_file (file_in IN VARCHAR2, line_in IN VARCHAR2 := NULL)
IS
  file_handle TEXT_IO.FILE_TYPE;
BEGIN
  /*
  || Open the file, write a single line and close the file.
  */
  return_value := TEXT_IO.FOPEN (file_in, 'W');
  IF line_in IS NOT NULL
  THEN
    TEXT_IO.PUT_LINE (file_handle, line_in);
  ELSE
    TEXT_IO.PUT_LINE (file_handle, 'Hola Mundo!!!');
  END IF;
  TEXT_IO.FCLOSE (return_value);
END;

5.2 Verificar la existencia de un fichero

Este programa verifica que el fichero, por el que se pregunta, existe o no.
FUNCTION file_exists (file_in IN VARCHAR2) RETURN BOOLEAN
IS
BEGIN
  /* Open the file. */
  file_handle := TEXT_IO.FOPEN (file_in, 'R');
  /* Return the result of a check with IS_OPEN. */
  RETURN TEXT_IO.IS_OPEN (file_handle);
EXCEPTION
  WHEN OTHERS
  THEN
    /* Check for a TEXT_IO-specific error. */
    IF SQLCODE = -302000
    THEN
      DECLARE
        errmsg VARCHAR2(540) := TOOL_ERR.MESSAGE;
      BEGIN
        /* Check for specific message. */
        RETURN errmsg LIKE '%Could not find file.';
      END;
    ELSE
      RETURN FALSE;
    END IF;
END;

5.3 Buscar una cadena en un archivo

La función INSTR es muy útil a ser tan útil, me imaginé que este mismo tipo de operación también sería muy práctico en los archivos. La función line_with_text devuelve el número de línea de un archivo que contiene el texto especificado. Una función de este tipo tendría una especificación como:
FUNCTION line_with_text (file_in IN VARCHAR2, text_in IN VARCHAR2) RETURN INTEGER;
Es decir, dado un nombre de archivo y un fragmento de texto, encontrar la primera línea en el archivo que contiene ese texto. Se puede llamar a la función de la siguiente manera:
IF line_with_text ('names.vp', 'Hanubi') > 0 THEN
  MESSAGE ('Hanubi está e el fichero');
END IF;
El problema con esta versión de line_with_text es su total falta de visión. ¿Qué debo hacer si quieres encontrar la segunda aparición en el archivo? ¿Qué hago si necesito para iniciar mi búsqueda de la décima línea? ¿Qué debo hacer si desea llevar a cabo un mayúsculas y minúsculas búsqueda? Ninguna de estas variaciones son compatibles ni contempladas.

Para line_with_text, tenga una visión más amplia su especificación sería:
FUNCTION line_with_text (file_in IN VARCHAR2,
  text_in IN VARCHAR2,
  occurrence_in IN INTEGER := 1,
  start_line_in IN INTEGER := 1,
  end_line_in IN INTEGER := 0,
  ignore_case_in IN VARCHAR2 := 'IGNORE_CASE')
RETURN INTEGER;
Una pequeña descripcion de cada parámetro:

  • file_in: The name of the file to be opened.
  • text_in: The chunk of text to be searched for in each line of the file.
  • occurrence_in: The number of times the text should be found in distinct lines in the file before the function returns the line number.
  • start_line_in: The first line in the file from which the function should start its search.
  • end_line_in: The last line in the file to which the function should continue its search. If zero, then search through end of the file.
  • ignore_case_in: Indicates whether the case of the file contents and text_in should be ignored when checking for its presence in the line.

Observe que todos los nuevos parámetros tienen valores por defecto, por lo que se puede llamar de la misma forma y con los mismos resultados que la primera versión:
IF line_with_text ('names.vp', 'Hanubi') > 0 THEN
  MESSAGE ('Hanubi está e el fichero');
END IF;
Ahora, sin embargo, también puedo llamarla de una manera mucho más especifica:
* Confirme que la función asignada a este usuario "SUPERVISOR".
line_with_text ( 'config.usr', 'SUPERVISOR PAPEL =')
* Buscar la segunda aparición de "ELIMINAR" a partir de la quinta línea.
line_with_text ( 'commands.dat', 'Eliminar', 2, 5)
* Compruebe que la tercera línea contiene un tipo de terminal pliego de condiciones.
line_with_text ( 'SETUP.CFG', 'termtype =', 1, 3, 3)
La función line_with_text más amplia quedaría así:
FUNCTION line_with_text
   (file_in IN VARCHAR2,
   text_in IN VARCHAR2,
   occurrence_in IN INTEGER := 1,
   start_line_in IN INTEGER := 1,
   end_line_in IN INTEGER := 0,
   ignore_case_in IN VARCHAR2 := 'IGNORE_CASE')
 RETURN INTEGER
 /*
 || An "INSTR" for operating system files. Returns the line number of
 || a file in which a text string was found.
 */
IS
  /* Handle to the file. Only will open if arguments are valid. */
  file_handle TEXT_IO.FILE_TYPE;
  /* Use local variable to hold this information. */
  ignore_case BOOLEAN := UPPER (ignore_case_in) = 'IGNORE_CASE';
  /* Holds a line of text from the file. */
  line_of_text VARCHAR2(1000);
  text_loc INTEGER;
  found_count INTEGER := 0;
  /* Boolean to determine if there are more values to read */
  no_more_lines BOOLEAN := FALSE;
  /* Function return value */
  return_value INTEGER := 0;
BEGIN
  /* Assert valid arguments. If any fail, return NULL. */
  IF file_in IS NULL OR text_in IS NULL OR
     occurrence_in <= 0 OR start_line_in < 1 OR end_line_in < 0
  THEN
    return_value := NULL;
  ELSE
    /* All arguments are fine. Open and read through the file. */
    file_handle := TEXT_IO.FOPEN (file_in, 'R');
    LOOP

      /* Get next line and exit if at end of file. */
      get_nextline (file_handle, line_of_text, no_more_lines);
      EXIT WHEN no_more_lines;
      /* Have another line from file. */
      return_value := return_value + 1;

      /* If this line is between the search range... */
      IF (return_value BETWEEN start_line_in AND end_line_in) OR
       (return_value >= start_line_in AND end_line_in = 0)
      THEN
        /* Use INSTR to see if text is present. */
        IF ignore_case
        THEN
          text_loc := INSTR (line_of_text, text_in);
        ELSE
          text_loc := INSTR (UPPER (line_of_text), UPPER (text_in));
        END IF;
        /* If text location is positive, have a match. */
        IF text_loc > 0 THEN
          /* Increment found counter. Exit if matches request. */
          found_count := found_count + 1;
          EXIT WHEN found_count = occurrence_in;
        END IF;
      END IF;
    END LOOP;
    TEXT_IO.FCLOSE (file_handle);
  END IF;
  IF no_more_lines THEN
    /* read through whole file without success. */
    return_value := NULL;
  END IF;
  RETURN return_value;
END;

5.4 Coger la línea 'n' de un archivo

Para ello utilizamos la siguiente función:
FUNCTION get_nth_line (file_in IN VARCHAR2, line_num_in IN INTEGER) RETURN VARCHAR2 IS
  /* Handle to the file. Only will open if arguments are valid. */
  file_handle TEXT_IO.FILE_TYPE;
  /* Count of lines read from the file. */
  line_count INTEGER := 0;
  /* Boolean to determine if there are more values to read */
  no_more_lines BOOLEAN := FALSE;
  /* Function return value */
  return_value VARCHAR2(1000) := NULL;
BEGIN
  /* Need a file name and a positive line number. */
  IF file_in IS NOT NULL AND line_num_in > 0 THEN
    /* All arguments are fine. Open and read through the file. */
    file_handle := TEXT_IO.FOPEN (file_in, 'R');
    LOOP
      /* Get next line from file. */
      get_nextline (file_handle, return_value, no_more_lines);
      /* Done if no more lines or if at the requested line. */
      EXIT WHEN no_more_lines OR line_count = line_num_in - 1;
      /* Otherwise, increment counter and read another line. */
      line_count := line_count + 1;
    END LOOP;
  TEXT_IO.FCLOSE (file_handle);
  END IF;
  /* Either NULL or contains last line read from file. */
  RETURN return_value;
END;

6. Enlaces de interés

Ejemplos TEXT_IO

7. Fuentes

Fuente original (DOC, ENG)

Categoría: PL/SQLCategoría: TEXT_IOCategoría: Forms Builder

7 comentarios:

  1. Excelente muchas gracias por los procedimientos. Yo utilice el procedimiento 5.2 Verificar la existencia de un fichero, pero necesitaba enviar un correo y luego mover el archivo a una carpeta de respaldo. Y el procedimiento me dejaba el archivo abierto y no me dejaba moverlo. Así que se puede modificar el procedimiento con este cambio:

    FUNCTION file_exists (file_in IN VARCHAR2) RETURN BOOLEAN
    IS

    file_handle TEXT_IO.FILE_TYPE;
    Existe Boolean;

    BEGIN
    /* Open the file. */
    file_handle := TEXT_IO.FOPEN (file_in, 'R');
    /* Return the result of a check with IS_OPEN. */
    Existe := TEXT_IO.IS_OPEN (file_handle);
    TEXT_IO.FCLOSE (file_handle);

    RETURN Existe;

    EXCEPTION
    WHEN OTHERS THEN
    /* Check for a TEXT_IO-specific error. */
    IF SQLCODE = -302000 THEN
    DECLARE
    errmsg VARCHAR2(540) := TOOL_ERR.MESSAGE;
    BEGIN
    /* Check for specific message. */
    TEXT_IO.FCLOSE (file_handle);
    RETURN errmsg LIKE '%Could not find file.';
    END;
    ELSE
    TEXT_IO.FCLOSE (file_handle);
    RETURN FALSE;
    END IF;
    END;

    ResponderEliminar
  2. Excelente!!! muy buena explicación.

    ResponderEliminar
  3. a mi no me compila el TEXT_IO.PUT ni el TEXT_IO.PUT_LINE

    ResponderEliminar
    Respuestas
    1. No entiendo muy bien a lo que te refieres, el paquete TExt_io es un paquete interno de oracle forms.

      Eliminar