ONCE.21. ACCESO A ARCHIVOS

Bajo esta sección vamos a estudiar una importante aplicación de los programas en AutoLISP, esto es, la posibilidad de gestionar directamente archivos de texto ASCII. Todo lenguaje de programación que se precie goza de esta característica, ya que le permite acceder a un archivo de texto para leerlo, crearlo o modificarlo.

Si nos damos cuenta, la totalidad de los archivos que hemos venido estudiando en este curso como archivos de personalización, .MNU, .LIN, .PAT, .AHP, .SCR, etcétera, son archivos de texto ASCII. Es por ello, que desde un programa en AutoLISP podremos acceder a sus líneas en cualquier momento, así como crear archivos propios de una manera automática.

 

ONCE.21.1. Fundamento teórico somero sobre el
acceso a archivos

A grandes rasgos, los archivos o ficheros de texto o datos a los que podemos acceder desde la mayoría de los lenguajes de programación son dos: archivos de acceso secuencial y archivos de acceso aleatorio o directo. La diferencia principal entre ambos estriba en la forma en la que almacenan los datos. Por un lado, los archivos de acceso secuencial guardan la información secuencialmente, es decir, dato tras dato organizados todo ellos en una serie de líneas. Tras cada línea se reconoce un retorno de carro con salto de línea (INTRO) y al final del archivo un marca de fin de archivo. Son archivos ASCII que si se abren con cualquier editor se pueden examinar fácilmente.

Por otro lado, los archivos de acceso directo tienen una forma más eficaz de almacenar la información. En la teoría del acceso a estos archivos entra en juego el concepto de campo clave. Los datos son guardados según un campo clave o principal que los ordena basándose en su condición. De esta forma, una serie de registros almacenados según un campo clave numérico, serán introducidos en el archivo en su posición correcta, única e inequívoca.

Como norma general pues, los programas que manejen archivos de acceso aleatorio o directo llegarán mucho antes a la información buscada por el usuario, mientras que los programas que manejen archivos de acceso secuencial verán incrementado su tiempo de acceso, ya que han de recorrer todo el fichero hasta dar con la información buscada. Sin embargo, este último tipo de archivos ocupa bastante menos espacio en disco, por la no organización de sus registros (unos detrás de otros), mientras que los archivos de acceso directo ocupan más espacio, ya que los datos se escriben ordenados según dicho campo clave, dejando espacio para los registros vacíos aún no rellenados.

AutoLISP sólo es capaz de acceder a ficheros de acceso secuencial. Estos ficheros, pueden ser archivos de texto como un archivo de menú o de definición de tipos de línea, o ficheros de datos —también de texto ASCII— que contengan, por ejemplo, coordenadas de puntos de un levantamiento topográfico (típico fichero de salida de una estación total, por ejemplo).

Los ficheros o archivos de texto que no guarden datos divididos en campos y registros, estarán escritos como su autor lo quiso hacer, sin ningún orden lógico aparente. Sin embargo, los ficheros de datos de salida de muchos utensilios (instrumentos topográficos, máquinas de control numérico, etc.) o programas (bases de datos, hojas de cálculo o el propio AutoCAD) sí guardan una estructura tipificada y normalizada. La mayoría de ellos dividen sus registros (líneas) por retornos de carro con salto de línea (INTRO) y separan sus campos (columnas) por algún carácter especial: una coma (formato CDF), un espacio blanco (formato SDF), u otros. Además, los campos suelen estar encerrados entre comillas dobles o simples si son cadenas o sin ningún carácter delimitador si son campos numéricos. Otros ficheros, separarán registros y campos, o encerrarán datos, con cualquier otro carácter o técnica.

Sea cual fuere la opción o el tipo de fichero, el programador en AutoLISP habrá de examinar antes bien su contenido para estudiar la forma de llegar a extraer los datos necesarios del mismo.

Por último decir que existen tres formas básicas de acceder a un fichero (enseguida veremos cómo se hace en AutoLISP) desde los lenguajes de programación: la de lectura, en la que un imaginario puntero se coloca al principio del archivo exclusivamente para leer datos; la de escritura, en la que el puntero se coloca también al principio para escribir datos, eliminando los que ya hay; y la de añadir datos, en la que el puntero se coloca al final del archivo para agregar datos al mismo, sin afectar a lo que ya está guardado. Es muy importante tener esta última característica en cuenta para evitar errores fatales para el usuario.

 

ONCE.21.2. Funciones para el manejo de archivos

A continuación iremos describiendo cada una de las funciones de las que disponemos en AutoLISP para acceder y/o manejar ficheros. Comenzaremos evidentemente por la manera de abrir uno de estos archivos. Para ello debemos recurrir a la función inherente OPEN, cuya sintaxis es:

(OPEN nombre_archivo modo)

Esta función abre un archivo para permitir el acceso a las funciones de entrada y salida de AutoLISP. OPEN devuelve un valor de descriptor de archivo que posteriormente utilizaremos para acceder a él, por lo que la práctica lógica consistirá en guardar dicho descriptor para ser posteriormente utilizado. Viene a ser algo parecido que lo que hacíamos con la función LOAD_DIALOG para guardar el índice devuelto y luego usarlo en el acceso al cuadro de diálogo.

El nombre de archivo que proporcionamos a OPEN será una cadena entrecomillada en la que se especificará el nombre, y en su caso (si no se encuentra en el directorio actual) la ruta de acceso completa, del archivo o fichero en cuestión. Por su lado, el argumento modo, indica la manera en que se abrirá el archivo, según la tabla siguiente (el modo también se indica entre comillas dobles):

Argumento modo -- Descripción

------------------------------------------------------------------------------------

"r" ---------------- Abre en modo lectura. Sólo se pueden leer o extraer datos del
-------------------- archivo. Si el archivo indicado no existe se devuelve
nil.

"w" ---------------- Abre en modo escritura. Se escriben datos en el archivo y, si
-------------------- ya existían otros datos, se sobreescriben. Si el archivo indicado
-------------------- no existe se crea.

"a" ---------------- Abre en modo aditivo. Se escriben datos en el archivo al final del
-------------------- mismo, tras los datos existentes si hay. Si el archivo indicado no
-------------------- existe se crea.


Veamos varios ejemplos:

(SETQ Archivo (OPEN "ejemplo.txt" "w"))
(SETQ Arch (OPEN "c:/clientes/datos/text/bilbao.dat" "a"))
(SETQ Des (OPEN "a:\\move.scr" "r"))
(SETQ Archivo (OPEN "acadiso.lin" "r"))

 

(CLOSE descriptor_archivo)

CLOSE cierra el archivo válido abierto identificado por su descriptor de archivo (el obtenido por OPEN). Una vez cerrado el archivo, se devuelve nil y ya no se puede hacer ninguna operación con él. El descriptor de archivo deja de ser válido y al volver a abrirse mediante OPEN cambiará.

Es necesario cerrar los archivos cuando ya no se van a utilizar. Ejemplo:

(CLOSE Archivo)


(READ-LINE [descriptor_archivo])

Una vez abierto un archivo para lectura, utilizaremos la función READ-LINE para leer una línea completa de dicho archivo, es decir, hasta el salto de línea. Para ello deberemos indicar el descriptor de archivo que devolvió la función OPEN al abrirlo. READ-LINE devuelve cada vez una de las líneas como cadena, es decir, entre comillas dobles, hasta que al llegar al final del archivo devuelve nil.

Por ejemplo, imaginemos un archivo de texto llamado DATOS.ML1 que se encuentra en el directorio raíz de un disco duro (por ejemplo C:\). Dicho archivo contiene las siguientes líneas:

Esto es una prueba
de lectura de archivos.

Primero pues deberemos abrirlo para lectura, guardando el descriptor de archivo en una variable que llamaremos Arch:

(SETQ Arch (OPEN "c:\\datos.ml1))

A continuación, si hacemos:

(READ-LINE Arch)

AutoLISP devuelve:

"Esto es una prueba"

Una siguiente instrucción igual READ-LINE devolverá:

"de lectura de archivos."

Una siguiente instrucción igual devolverá nil, ya que se ha llegado al final del archivo. Si se desea retornar el puntero de lectura al principio del archivo, es necesario cerrarlo y volverlo a abrir, si no, como observamos, el puntero se sitúa en la siguiente línea a la última leída.

El siguiente pequeño programa comprueba si el tipo de línea TRAZO_Y_PUNTOX2 se encuentra definido en el archivo ACADISO.LIN de AutoCAD 14:

(DEFUN C:BuscaLin ()
--(SETQ Existe nil)
--(SETQ Arch (OPEN "c:\\archiv~1\\autoca~1\\support\\acadiso.lin" "r"))
--(WHILE (SETQ Lin (READ-LINE Arch))
----(SETQ Lin (STRCASE Lin T))
----(IF (WCMATCH Lin "*trazo_y_puntox2*")
------(SETQ Existe T)
------(IF Existe () (SETQ Existe nil))
----)
--)
--(IF Existe
----(PROMPT "\nExiste.")
----(PROMPT "\nNo existe.")
--)
--(CLOSE Arch)
--(PRIN1)
)

En un principio se establece la variable Existe como nil, para futuras ejecuciones del programa, y se abre el archivo en cuestión para lectura. Se establece una condicional que dice que mientras exista la variable Lin, que almacena una línea leída del archivo, se realiza todo lo demás. Es decir, mientras Lin tenga valor, es igual aquí a mientras queden líneas por leer en el archivo. En el momento en que Lin valga nil (fin de archivo) ya no se ejecutará ninguna función de la repetitiva condicional.

En esa repetitiva se convierte el contenido de Lin (cada línea del archivo) a minúsculas con STRCASE y la opción T. Esto se realiza porque al ser una cadena se distingue entre mayúsculas y minúsculas, de tal forma que al intentar buscar posteriormente la subcadena trazo_y_puntox2 en Lin (con WCMATCH), no se produzca ningún error de interpretación.

Si la subcadena buscada existe se establece Existe como T, y si no existe se mira si Existe ha tomado alguna vez el valor T (ha existido). Si así fuera no se haría nada, si no se establecería a nil.

Una vez terminado de leer el fichero, se comprueba si la variable Existe tiene valor T o nil (existe o no) y se actúa en consecuencia, emitiendo el mensaje adecuado. Se termina cerrando el archivo y con un PRIN1 para evitar el nil de CLOSE.

Si a la función READ-LINE no se le especifica un descriptor de archivo, lee una cadena de texto completa desde el teclado. Así por ejemplo, si escribimos en línea de comandos:

(READ-LINE)

y ahora escribimos en la propia línea de comandos Prueba de lectura desde teclado., AutoLISP devuelve:

"Prueba de lectura desde teclado.".


(WRITE-LINE cadena [descriptor_archivo])

WRITE-LINE funciona de forma inversa a READ-LINE, esto es, escribe la cadena indicada como una línea completa en el archivo especificado por su descriptor válido. Evidentemente el archivo deberá estar abierto para escribir o añadir datos, dependiendo de la modalidad que nos interese.

El siguiente programa permite crear automáticamente archivos de guión:

(DEFUN C:CreaSCR ()
--(SETQ NombreArchivo (GETSTRING "Introduzca nombre de archivo: "))
--(SETQ DesArchivo (OPEN NombreArchivo "a"))
--(WHILE (/= (SETQ Línea (GETSTRING "Introduzca comando (INTRO fin): " T)) "")
----(WRITE-LINE Línea DesArchivo)
--)
--(CLOSE DesArchivo)
)

Tras introducir un nombre para el archivo y abrirlo, se piden continuamente los comandos necesarios, los cuales se van escribiendo al archivo en cuestión. Al introducir un INTRO se termina el programa —ya que Línea es igual a una cadena nula ("")— y se cierra el archivo.

En este tipo de programas es lógico introducir bastantes controles, por ejemplo para que el usuario introduzca la ruta de acceso con contrabarras y el propio programa las cambie a formato de AutoLISP; es cuestión de jugar con las variables de manejo de cadenas estudiadas.

WRITE-LINE devuelve la cadena en cuestión entre comillas. Si no se indica un descriptor de archivo, la función escribe la cadena, capturada desde el teclado, en la línea de comandos.

(READ-CHAR [descriptor_archivo])

La siguiente función que veremos, READ-CHAR, lee un carácter del archivo especificado (abierto para lectura) por su descriptor cada vez, y devuelve su código ASCII. Así por ejemplo, si un archivo de texto llamado EJEMPLO.DAT (en C:\) contuviera las dos siguientes líneas:

Hola
Hola yo

y si se abriera así:

(SETQ Hola (OPEN "c:/ejemplo.dat" "r"))

sucesivas llamadas con READ-CHAR así:

(READ-CHAR Hola)

devolverían lo siguiente:

72
111
108
97
10
72
111
108
97
32
121
111
nil

Cada vez se lee un carácter más y se devuelve su código ASCII. El código 10 corresponde al INTRO entre las dos líneas (retorno de carro con salto de línea). Por compatibilidad con UNIX, en el que el final de línea es siempre el código ASCII 10, READ-CHAR devuelve este código cuando se utiliza en plataformas DOS/Windows y se detecta un INTRO, a pesar de que en realidad el código ASCII de este carácter es el 13. Véase la lista de códigos ASCII en el APÉNDICE F. Cuando no hay más caracteres, READ-CHAR devuelve nil.

Como sabemos, para volver a leer el archivo desde el principio (el puntero de lectura se coloque en el inicio) deberemos cerrarlo y volverlo a abrir.

NOTA: Para poder tratar con estos códigos ASCII, recuérdense las funciones ASCII y CHR de conversión carácter/código y código/carácter respectivamente, estudiadas en la sección ONCE.12..

Si a la función READ-CHAR no le acompaña un descriptor de archivo, lee un carácter de la memoria temporal de entrada del teclado (buffer del teclado) y devuelve su código ASCII. Si el buffer contiene varios caracteres en el momento de ejecución de READ-CHAR, devuelve el primero de ellos. Al igual que con los ficheros de texto, sucesivas llamadas devolverán los siguientes caracteres. Si no hay ningún carácter en el buffer de teclado en dicho momento, READ-CHAR espera a que el usuario entre una serie de caracteres, finalizados con INTRO. Entonces devuelve el primero de los caracteres introducidos; los siguientes READ-CHAR devolverán el resto.

El siguiente ejemplo de READ-CHAR (combinado con READ-LINE) controla si el primer carácter de cada línea de un archivo .LSP es un punto y coma (;). Si así fuera agrega una unidad a un contador. Al final, muestra el número de líneas íntegras de comentarios que existen en el archivo:

(DEFUN C:Coment ()
--(SETQ Contador 0)
--(SETQ Archivo (GETSTRING "\nCamino y nombre del archivo .LSP: "))
--(SETQ Desc (OPEN Archivo "r"))
--(WHILE (SETQ Lin (READ-CHAR Desc))
----(IF (= (CHR Lin) ";")
------(PROGN
--------(SETQ Contador (1+ Contador))
--------(READ-LINE Desc)
------)
------(READ-LINE Desc)
----)
--)
--(CLOSE Desc)
--(PROMPT (STRCAT "\nEl total de líneas íntegras de comentarios es: " (ITOA
--------------------Contador) "."))
--(PRIN1)
)

Obsérvese la manera conjunta de trabajar READ-CHAR y READ-LINE. READ-CHAR lee el primer carácter de la línea, si es un punto y coma (convertido el código con CHR) añade una unidad al contador y con READ-LINE se lee el resto de la línea. De esta manera hacemos que el puntero de lectura se coloque al principio de la siguiente línea, con lo que podemos volver a empezar. En el caso en el que el primer carácter no sea un punto y coma, se realiza la misma función con READ-LINE pero sin incrementar el contador.

(WRITE-CHAR código_ASCII [descriptor_archivo])

WRITE-CHAR realiza la función inversa a READ-CHAR, es decir, escribe en un archivo cuyo descriptor se especifica, o en pantalla si no se especifica ningún descriptor, el carácter cuyo código ASCII se indica. Además, devuelve este código ASCII.

Así pues, si escribimos:

(WRITE-CHAR 72 Desc)

siendo Desc un descriptor válido de archivo, en dicho archivo se escribirá H, esto es, el carácter correspondiente al código ASCII 72. Si escribiéramos en línea de comandos:

(WRITE-CHAR 72)

AutoLISP devolvería:

H72

ya que, como hemos dicho, escribe el carácter y devuelve el código.

Por las mismas razones de compatibilidad con UNIX que en el caso de READ-CHAR, tanto el código ASCII 10 como el 13 pueden indicarse para escribir retornos de carro con salto de línea (INTRO) o fines de línea.

Veamos ahora otras tres funciones muy útiles para escribir datos en un archivo. La primera es PRIN1, y las otras dos derivadas (PRINT y PRINC) sin similares a ella con alguna pequeña diferencia que explicaremos. La sintaxis de PRIN1 es:

(PRIN1 [expresión [descriptor_archivo]])

Esta función escribe expresiones en un archivo, si se indica un descriptor válido, o en la línea de comandos, si no se indica descriptor alguno. Devuelve la propia expresión.

A diferencia de WRITE-LINE y WRITE-CHAR, PRIN1 permite escribir cualquier expresión en un fichero, sin necesidad de que sea una cadena de texto. Así por ejemplo, la siguiente secuencia:

(SETQ Archivo (OPEN "c:/datos.dat" "w"))
(SETQ Mensualidad (* 2 2350))
(PRIN1 "Mensualidad" Archivo)
(PRIN1 Mensualidad Archivo)
(CLOSE Archivo)

abre un archivo para escritura. Asigna a la variable Mensualidad el valor de un producto y, a continuación, escribe en dicho archivo un texto o cadena fija y el valor de la variable expuesta. Por último, el fichero es cerrado. La apariencia ahora del archivo DATOS.DAT sería la que sigue:

"Mensualidad"4700

Es decir, las expresiones se añaden sin separación de líneas o interlineado y, además las cadenas literales son incluidas con sus comillas dobles correspondientes, al contrario que con WRITE-LINE. Por el contrario, si las expresiones son literales con apóstrofo:

(PRIN1 ’Mensualidad Archivo)
(PRIN1 ’(SetQ x 5.5) Archivo)

se añadirían como tales, pero en mayúsculas, evidentemente:

MENSUALIDAD(SETQ X 5.5)

Si la expresión es una cadena con caracteres de control, PRIN1 escribe esos caracteres con contrabarra y el número de su código octal. Por ejemplo:

(PRIN1 (CHR 2) Archivo) escribe y devuelve "\002"
(PRIN1 (CHR 10) Archivo) escribe y devuelve "\n"
(PRIN1 (CHR 13) Archivo) escribe y devuelve "\r"

Se puede utilizar PRIN1 sin ningún argumento, que es lo que venimos haciendo en los programas hasta ahora. De esta forma, la función devuelve una cadena nula, es decir, simplemente salta una línea en la línea de comandos de AutoCAD 14, sin ningún otro mensaje. Al incluir pues PRIN1 sin argumentos como última expresión de un programa, haremos que su final sea "limpio". Esta particularidad no se puede utilizar con archivos evidentemente.

NOTA: La utilidad real de PRIN1 es la de escribir expresiones compatibles por ejemplo con LOAD o con COMMAND, que llama, este último, a comandos de AutoCAD cuyos mensajes con admiten todos los códigos ASCII.

(PRINT [expresión [descriptor_archivo]])

Totalmente idéntica a PRIN1, salvo que salta a nueva línea antes de visualizar o escribir la expresión y añade un espacio blanco al final. La siguiente secuencia:

(SETQ Archivo (OPEN "c:/datos.dat" "w"))
(SETQ Mensualidad (* 2 2350))
(PRINT "Mensualidad" Archivo)
(PRINT Mensualidad Archivo)
(CLOSE Archivo)

escribiría:

 

"Mensualidad"
4700

dejando la primera línea en blanco, ya que antes de escribir la cadena salta una línea también. Al final de ambas líneas hay un espacio blanco.

(PRINC [expresión [descriptor_archivo]])

Totalmente idéntica a PRIN1, salvo que los caracteres de control se escriben como tales, no representados por su código octal. Alguno de estos caracteres puede ser representado en el archivo por un símbolo, cosa que apreciaremos al abrirlo con un editor ASCII.

A diferencia de PRIN1, la función PRINC escribe cualquier carácter admitido en un archivo de texto y las expresiones pueden ser leídas directamente con funciones como READ-LINE.

Por ejemplo las siguientes inclusiones del código ASCII del salto de línea con retorno de carro, no aparecerán en octal en el archivo, sino que serán caracteres INTRO verdaderos, es decir, se producirán los saltos de línea con sus retornos de carro:

(SETQ k (OPEN "c:/ejem1.doc" "a"))
(PRIN1 "w" k)
(PRINC (CHR 10) k)
(PRIN1 "x" k)
(PRIN1 "y" k)
(PRINC (CHR 10) k)
(PRIN1 "z" k)

El resultado será:

"w"
"x""y"
"z"

Y veamos por último otras dos funciones muy utilizadas.

(FINDFILE nombre_archivo)

La función FINDFILE explora directorios en busca del archivo especificado. El archivo se indica entre comillas por ser cadena. Si se especifica sin ruta o camino de acceso, FINDFILE buscará en los caminos de archivos de soporte incluidos en el cuadro Preferencias, en la pestaña Archivos (carpeta Camino de búsqueda de archivo de soporte). Si se escribe una ruta de acceso en el argumento nombre_archivo, FINDFILE buscará el archivo en la ruta especificada.

Si la función FINDFILE encuentra el archivo buscado devuelve la cadena que indica su camino y nombre en formato válido de AutoLISP, si no es así, devuelve nil.

FINDFILE se utiliza eminentemente para comprobar la existencia de archivos o ficheros indicados por el usuario para ser abiertos, ya que de no existir se produciría un error en tiempo de corrida de AutoLISP. Vamos a ver un ejemplo ya explicado en el que se ha añadido esta nueva característica; se corresponde con el programa contador de líneas íntegras de comentarios en los ficheros .LSP:

(DEFUN C:Coment ()
--(SETQ Contador 0)
--(SETQ Archivo (GETSTRING "\nCamino y nombre del archivo .LSP: "))
--(IF (NOT (FINDFILE Archivo))
----(PROGN
------(PROMPT "\nError: el archivo especificado no existe.\n")
------(EXIT)
----)
--)
--(SETQ Desc (OPEN Archivo "r"))
--(WHILE (SETQ Lin (READ-CHAR Desc))
----(IF (= (CHR Lin) ";")
------(PROGN
--------(SETQ Contador (1+ Contador))
--------(READ-LINE Desc)
------)
------(READ-LINE Desc)
----)
--)
--(CLOSE Desc)
--(PROMPT (STRCAT "\nEl total de líneas íntegras de comentarios es: " (ITOA
-------------------Contador) "."))
--(PRIN1)
)


(GETFILED título_letrero archivo_defecto patrón_extensión modo)

La función GETFILED muestra el letrero estándar de gestión de archivos de AutoCAD 14. Puede ser muy útil cuando se programe con cuadros de diálogo en DCL, ya que es más vistoso escoger un archivo de un letrero que escribirlo en línea de comandos.

Esta función devuelve una cadena que contiene la ruta y el nombre del archivo seleccionado en el cuadro. Este nombre y ruta están en formato AutoLISP, por lo que pueden ser perfectamente guardados en una variable y posteriormente utilizados por la función OPEN, por ejemplo. Expliquemos los argumentos.

· título_letrero es una cadena que formará el nombre en la parte superior el cuadro, en la barra de título (azul comúnmente). Si se indica una cadena nula (""), aparecerá el título por defecto Abrir dibujo.

· archivo_defecto es el archivo que aparecerá en la casilla Nombre de archivo: por defecto. Su extensión será la indicada con el siguiente argumento, si se indica. Si se introduce cadena nula (""), no aparecerá ningún archivo por defecto.

· patrón_extensión especifica la extensión o extensiones que admitirá el cuadro. Se pueden indicar varias separadas por punto y coma (mnu;mns;mnl;*), siendo la primera de ellas la que aparecerá primera en la lista desplegable Tipos de archivos: y la que aparecerá por defecto en la casilla Nombre de archivo:, siempre que haya un archivo indicado por defecto. Si se indica una cadena nula (""), se especifica el patrón Todos los archivos (*.*). Este patrón también se consigue con una cadena que encierre un único asterisco "*".

· modo es un valor entero (codificado en bits) que condiciona el letrero según los valores siguientes:

Valor de modo --------- Descripción

-------------------------------------------------------------------------------------------------------

0 No es en realidad un valor de bit. Se define cuando no se indica ninguno de los siguientes. El archivo seleccionado habrá de existir y en el botón de la derecha de la casilla Nombre de archivo: apa-recerá la leyenda Abrir, ya que es un cuadro para leer de un archivo.

1 El archivo seleccionado habrá de ser nuevo, es decir, será un archivo que se creará. Por lo tanto, lo lógico será escribir un nombre de archivo no existente en la casilla Nombre de archivo:, tras escoger el directorio donde se creará. A la derecha de esta casilla la leyenda del botón adjunto será Guardar, ya que es un cuadro para escribir en un archivo. Si se elige un archivo existente aparecerá un mensaje de error advirtiendo de su existencia, ofreciéndonos la posibilidad de sobreescribirlo. El valor 1 es el bit 0.

2 Desactiva el botón Teclearlo. Este bit se define si se llama a la función GETFILED mientras otro cuadro de diálogo está activo (en caso contrario, obliga a cerrar el otro cuadro de diálogo). Si no se define este bit, se activa el botón Teclearlo. Cuando el usuario selecciona el botón, el cuadro de diálogo desaparece y GETFILED devuelve 1. El valor 2 es el bit 1.

4 Permite al usuario escribir una extensión de nombre de archivo arbitraria o bien no escribir ninguna. Si no se define este bit, GETFILED sólo acepta la extensión o extensiones especificadas en el argumento patrón_extensión, y si el usuario la escribe en la casilla de texto Nombre de archivo:, la añade al nombre del archivo. El valor 4 es el bit 2.

8 Si se define este bit y no se define el bit 0 (valor 1), GETFILED inicia en la biblioteca una búsqueda del nombre de archivo escrito. Si encuentra el archivo y el directorio en el orden de búsqueda en la estructura, descompone el camino y sólo devuelve el nombre del archivo. Esto no ocurre si los archivos que se buscan tienen el mismo nombre pero se encuentran en distintos directorios. Si no se define este bit, GETFILED devuelve el nombre completo del archivo, incluido el nombre del camino. Definiremos este bit si utilizamos el cuadro de diálogo para abrir un archivo existente cuyo nombre deseamos guardar en el dibujo (u otra base de datos). El valor 8 es el bit 3.


Los valores mencionados pueden sumarse para combinar las distintas acciones, pudiendo pues indicar valores desde
0 hasta 15 para el argumento modo.

Si se abre un cuadro de gestión de archivos para abrir uno (con valor de modo 0, por ejemplo) y la extensión patrón única o principal (la primera) es dwg, al cuadro se le añade un anexo por la derecha para mostrar una presentación preliminar del dibujo de AutoCAD 14, si la tuviere.

Como ya se ha dicho, la devolución de GETFILED está en formato AutoLISP, concretamente con doble contrabarra (contrabarra carácter de control y carácter contrabarra) para separar directorios, por lo que una secuencia como la que sigue, por ejemplo, sería totalmente válida:

(SETQ Archivo (GETFILED "Abrir archivo ASCII" "" "txt;dat;*" 0))
(SETQ Des (OPEN Archivo "r"))

 

19ª fase intermedia de ejercicios

· Crear un programa para realizar conversiones de unidades con respecto a las existentes en el archivo ACAD.UNT. Presentará un menú textual en el que se podrá escoger la categoría de las unidades y, posteriormente, el tipo de unidad de origen y el de destino.

· Realizar un programa que permita crear de un modo automático tipos de línea complejos con texto. Se solicitarán al usuario todos los datos necesarios y después de presentará una muestra del tipo de línea creado. Tras la aceptación por parte del usuario, se incluirá la definición en el archivo indicado por él también.

 

ONCE.22. FUNCIONES DE CHEQUEO

Se estudian ahora las funciones de AutoLISP que se utilizan para examinar los símbolos de los programas, sean variables de usuario, subrs, valores concretos, funciones, etcétera, y detectar una característica en concreto. Se puede conocer de este modo si un dato es cero o no, si una variable existe o es nil, y demás.

Todas estas funciones las hemos venido sustituyendo por otros mecanismos totalmente lícitos y que, en el fondo, se utilizan más. Sin embargo, hemos de conocerlas e incluso veremos alguna que nos descubra nuevas posibilidades.

La primera que veremos se corresponde con la sintaxis:

(ATOM elemento)

ATOM examina el elemento indicado como argumento único y devuelve T si se trata de un átomo o nil si es una lista. Todo lo que no sea una lista se considera un átomo. Veamos algunos ejemplos:

(ATOM ’(3 5)) devuelve nil
(ATOM 6)
devuelve T
(ATOM k)
devuelve T
(ATOM (SETQ x 6.7))
devuelve T
(ATOM ’(SETQ x 6.7))
devuelve nil

(BOUNDP elemento)

Esta función devuelve T si el símbolo indicado está asociado a un valor distinto de nil. El símbolo puede ser una variable, un nombre de función de usuario o incluso el nombre de una subrutina de AutoLISP. Únicamente si el símbolo es nil, BOUNDP devuelve nil.

Puesto que BOUNDP examina símbolos, hay que indicar estos sin evaluar, es decir precedidos del carácter de QUOTE: el apóstrofo (’). Veamos unos ejemplos; supongamos las siguientes declaraciones:

(SETQ x 24)

(DEFUN FuncMi () (SETQ n (1+ n)))
(SETQ y x)

Veamos los resultados de la aplicación de BOUNDP:

(BOUNDP ’x) devuelve T
(BOUNDP ’FuncMi)
devuelve T
(BOUNDP ’y)
devuelve T
(BOUNDP ’h)
devuelve nil
(BOUNDP ’FuncOt)
devuelve nil
(BOUNDP ’getpoint)
devuelve T

Esta función puede resultarnos útil en ciertos momentos. Quizá para saber si una variable es nil o T no la utilizaremos nunca, ya que estas expresiones son iguales:

(IF Radio...
(IF (BOUNDP ’Radio)...

Pero estudiemos un ejemplo: quizás un programa necesite saber en tiempo real si una variable a la que se va a dar un valor existe como función inherente de AutoLISP. Sabemos que no debemos utilizar variables que sean nombres de funciones AutoLISP, ya que perderán, las funciones, su utilidad y valor. Sabemos también que existe la función ATOMS-FAMILY que nos permite saber si cierta subr existe o no, o incluso si un nombre de variable está ya utilizado o no. Puede que ATOMS-FAMILY sea más engorroso de utilizar que BOUNDP dentro de un programa.

En estos casos podremos utilizar BOUNDP con este objetivo: conocer si una variable que va a ser creada en tiempo real existe ya, sea de usuario o como nombre de inherente de AutoLISP, o no.

 

(LISTP elemento)

LISTP, por su lado, explora el elemento indicado y devuelve T en el caso de que se trate de una lista y nil si no lo es. Supongamos:

(SETQ JogtGt ’(10 25 0))
(SETQ x 45)

Veamos los resultados:

(LISTP JogtGt) devuelve T
(LISTP ’JogtGt)
devuelve nil
(LISTP x)
devuelve nil
(LISTP (LIST y z))
devuelve T

 

(NUMBERP elemento)

NUMBERP examina el resultado de la evaluación del elemento indicado como argumento y devuelve T si dicho resultado es un número entero o real. De lo contrario devuelve nil. Supongamos:

(SETQ x 25)
(SETQ a "Hola, ¿qué tal?")

Veamos los resultados:

(NUMBERP x) devuelve T
(NUMBERP a)
devuelve nil
(NUMBERP (SETQ y (+ 4.5 67)))
devuelve T
(NUMBERP ’x)
devuelve nil

Una función que puede resultar interesante a la hora de operar con ciertas variables de las cuales no conocemos su tipo de contenido exacto.


(MINUSP elemento)

MINUSP devuelte T si el valor del elemento indicado es un número real o entero negativo. En caso contrario devuelve nil. Ejemplos:

(MINUSP –5) devuelve T
(MINUSP –1.8936)
devuelve T
(MINUSP (* 2 5))
devuelve nil
(MINUSP (+ –5 6))
devuelve nil
(MINUSP (+ –5 4))
devuelve T
(MINUSP (SETQ z –78))
devuelve T
(ZEROP elemento)

ZEROP devuelte T si el valor del elemento indicado es cero; en caso contrario devuelve nil. El resultado ha de ser numérico, si no se produce un error bad argument type. Supongamos:

(SETQ x 25)
(SETQ y 0)

Vemos ahora los resultados:

(ZEROP x) devuelve nil
(ZEROP y)
devuelve T
(ZEROP (- x x))
devuelve T
(ZEROP (* y –1))
devuelve T

 

 

(NULL elemento)

Esta función examina el valor asociado al elemento y devuelve T si dicho valor es nil. En caso contrario devuelve nil.

Existe una diferencia importante entre esta función y BOUNDP. En este caso se examinan resultados de evaluación de símbolos y no los propios símbolos. Por eso no interesa indicar literales con NULL, puesto que el resultado de la evaluación de un literal de un símbolo es el propio nombre del símbolo. Como ese valor no es nil, NULL aplicado a literales devolverá siempre nil.

Supongamos las siguientes igualdades y la función definida:

(SETQ x 35)
(DEFUN GoiBeh () (SETQ n (1+ n)))
(SETQ y x)

Veamos los distintos resultados:

(NULL x) devuelve nil
(NULL GoiBeh) devuelve nil
(NULL ’GoiBeh) devuelve nil
(NULL ghtF) devuelve T
(NULL w) devuelve T

Veremos ahora una función importante a la que podemos sacar mucho partido.


(TYPE elemento)

La función TYPE devuelve el tipo de elemento indicado. Este elemento puede ser un símbolo, un valor concreto, una expresión AutoLISP, un nombre de función...

TYPE devuelve el nombre del tipo atendiendo a la siguiente tabla:

Devolución -------- Significado

------------------------------------------------------------

REAL --------------- Valor numérico real.

INT --------------- Valor numérico entero.

STR --------------- Valor textual (cadena alfanumérica).

FILE -------------- Descriptor de archivo.

PICKSET ---------- Conjunto de selección de AutoCAD.

ENAME ------------ Nombre de entidad de AutoCAD.

SYM --------------- Símbolo (variable).

LIST -------------- Lista o función de usuario.

SUBR -------------- Función inherente de Autolisp o subrutina.

PAGETB ----------- Tabla de paginación de funciones.

EXSUBR ----------- Subrutina externa.

Supongamos las siguientes declaraciones como ejemplo:

(SETQ x 53 y 27.5 Txt "HOLA" Lista1 ’(a b c))
(SETQ IndArch (OPEN "prueba.lsp" "a"))
(SETQ Conj (SSGET) NomEnt (SSNAME Conj 0))

Veamos ahora como responderían los diversos elementos a una función TYPE:

(TYPE x) devuelve INT
(TYPE ’x)
devuelve SYM
(TYPE y)
devuelve REAL
(TYPE IndArch)
devuelve FILE
(TYPE Conj)
devuelve PICKSET
(TYPE NomEnt)
devuelve ENAME
(TYPE Txt)
devuelve STR
(TYPE Lista1)
devuelve LIST
(TYPE setq)
devuelve SUBR
(TYPE (SETQ z (+ 1 3)))
devuelve INT

NOTA: Las tablas de paginación (tipo PAGETB) son elementos que se añaden como primer término de las listas que definen funciones de usuario, cuando la paginación virtual de funciones se encuentra activada. De todo esto se hablará al explicar la función VMON en la sección ONCE.24..

El tipo devuelto por TYPE siempre está en mayúsculas y es un nombre no una cadena (no está entrecomillado).

 

ONCE.22.1. Rastreo

Para acabar esta sección veremos dos últimas funciones denominadas de rastreo. Se llaman así porque se dedican a la parte de depuración de un programa en AutoLISP. Estas dos funciones se llaman TRACE y UNTRACE.

(TRACE función1 [función2...])

TRACE marca las funciones indicadas con un atributo de rastreo y devuelve el nombre de la última función. La forma de utilizar TRACE es, generalmente, escribiéndola en la línea de comando de AutoCAD. Una vez definidas así las funciones que queremos rastrear, ejecutamos el programa en cuestión.

Cada vez que la función indicada en TRACE es evaluada se visualiza en pantalla la llamada a esa función y, cuando termina, el resultado de la evaluación. Las distintas llamadas aparecen con sangrados de línea proporcionales al nivel de anidación de las funciones. El texto que indica la entrada en la función es Entrada de Función:, siendo en las versiones anglosajonas del programa: Entering Función: (Función se refiere al nombre de la función). El resultado se precede del texto fijo Resultado: (Result: en inglés).

NOTA: El nombre de la función utilizada en TRACE ha de ser uno de una función definida por el usuario. TRACE se puede utilizar antes de cargar el programa en memoria o después.

(UNTRACE función1 [función2...])

Esta función desactiva los atributos de rastreo activados por TRACE. Devuelve el nombre de la última función indicada.

Para aclarar el significado de estas funciones veamos un pequeño ejemplo. Supongamos el siguiente programa:

(DEFUN PidePto ()
--(SETQ pt2 (GETPOINT pt1 "Nuevo punto: "))(TERPRI)
--(COMMAND "linea" pt1 pt2 "")
--(SETQ pt1 pt2)
)

(DEFUN C:DibuLin ()
--(SETQ pt1 (GETPOINT "Introducir punto: "))(TERPRI)
--(REPEAT 4
----(PidePto)
--)
)

Si hiciéramos:

(TRACE PidePto)

y ejecutáramos el nuevo comando de AutoCAD DIBULIN, el resultado podría ser:

----Entrada de PIDEPTO:
Comando: Resultado: (200.0 50.0 0.0)
----Entrada de PIDEPTO:
Comando: Resultado: (205.0 65.0 0.0)
----Entrada de PIDEPTO:
Comando: Resultado: (230.0 70.0 0.0)
----Entrada de PIDEPTO:
Comando: Resultado: (250.0 100.0 0.0)

Cada vez que se llama a la función dentro de la repetitiva (REPEAT 4), esto es un total de cuatro veces, aparece el mensaje Entrada de PIDEPTO:, que aparece con dos sangrados sucesivos, porque esta llamada está incluida dentro del DEFUN C:Dibulin y a su vez dentro de REPEAT 4, por lo tanto en un segundo nivel de anidación. Una vez evaluada la función se visualiza el resultado.