Ficheros con el TR-dos ========================================================================= El sigte articulo está dividido en dos partes. El primero es una adaptación del articulo "No lo olvides, archivalo" pensado esta vez en el Tr-dos. La segunda parte está dedicada exclusivamente al Tr-dos v5.xx en adelante. I - No lo olvides, archivalo... con TR-dos. ------------------------------------------- (texto condensado y adaptado originalmente publicado en Input Sinclair) Todo programa que maneje gran cantidad de datos ha de contar con alguna forma conveniente de almacenar los datos. Una forma es usar sentencias DATA para almacenar un listín telefonico sencillo, pero si la informacion queda dentro del programa mismo, uno tiene que interrumpir el programa (con BREAK) para modificarlo, lo cual no es muy satisfactorio. Es mucho mejor almacenar la informacion en cinta o disco, los datos en este caso se almacenan en un fichero. Guardando los datos separadamente del "nucleo" del programa se tiene loq ue se llama una memoria virtual: es decir que el programa base puede ser usado con ficheros diferentes, cada uno con su propio numero de registros o campos. El almacenamiento secuencial de datos es uno de los metodos preferidos, ya que su programacion es sencilla y los principios en que se basa son faciles de entender y los datos pueden ser convertidos a un formato que permite su traslado de un programa a otro o de un ordenador a otro inclusive. En un fichero secuencial los datos se almacenan en serie, con un solo byte por separador entre datos. Este articulo explica como crear tus propios ficheros de modo de poderlos incorporar en tus programas. Creacion de fichero: "Listin telefonico" En este programa ejemplo pensado para el Betadisco con TR-dos vrs: 1.xx - 4.xx, las entradas admitidas son nombre, apellido y numero. La informacion puede ser enviada directamente al fichero, pero es mas conveniente almacenarlo en cadenas para salvar posteriormente toda ella. La primera parte del programa, establece un sencillo bucle que permite introducir los datos. El maximo da datos a ingresar es 50, si se desea más, se deben cambiar las dimensiones de la línea 10: 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12): DIM n(1): DIM s$(15) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n)<>s$ AND n<50 THEN GO TO 30 Para salvar los datos como matriz: 80 CLS : PRINT "Almacenando los datos" 100 LET n(1)=n: Randomize Usr 15363: Rem: SAVE "contador" DATA n() 110 Randomize Usr 15363: Rem: SAVE "nombre" DATA a$() 120 Randomize Usr 15363: Rem: SAVE "apellido" DATA b$() 130 Randomize Usr 15363: Rem: SAVE "telefono" DATA t$() 140 PRINT "Datos almacenados" 150 STOP Si se desea salvar los datos a cinta, omitir entonces todos los "Randomize Usr 15363: Rem:" del listado ya que de lo contrario, se salvará todo al disco A por defecto. Lectura del fichero: La rutina para leer el contenido de un fichero y trasladarlo a la memoria, es el inverso al de escritura. Para cargar la matriz de datos de cinta o disco, tipear: 200 CLEAR 210 Randomize Usr 15363: Rem: LOAD "contador" DATA n() 220 Randomize Usr 15363: Rem: LOAD "nombre" DATA a$() 230 Randomize Usr 15363: Rem: LOAD "apellido" DATA b$() 240 Randomize Usr 15363: Rem: LOAD "telefono" DATA t$() 250 FOR l=1 TO n(1) 260 PRINT a$(l),b$(l),t$(l) 270 NEXT l En el caso del BETA128 reemplazar todos los Randomize anteriores por: "Randomize Usr 15619: Rem:" Una mejora al programa es añadirle la opcion de elegir en cuál unidad se desea salvar los datos (en el caso de tener más de una unidad de disco): 90 INPUT "Disco (A/B)";j$ 95 IF j$="A" THEN Randomize Usr 15363: Rem: *"A" 97 IF j$="B" THEN Randomize Usr 15363: Rem: *"B" El metodo anterior SAVE DATA (unica alternativa con el TR-dos vrs: 1.xx - 4.xx ya que carece de ordenes OPEN/CLOSE para manejar ficheros) tiene el inconveniente de que hay que guardar toda la cadena aunque solo se use una parte de la misma. La versión que sigue a continuación, pensada para el TR-dos v. 5.xx. de la interface Beta128, almacena cada elemento de la cadena separadamente y se detiene cuando los datos se acaban. 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12): DIM s$(15) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n)<>s$ AND n<50 THEN GO TO 30 80 CLS : PRINT "Almacenando los datos" 110 Randomize Usr 15619: Rem: OPEN #4,"fichero",W 120 PRINT #4;n 125 FOR l=1 TO n 130 PRINT #4;a$(l)'b$(l)'t$(l) 140 NEXT l 150 Randomize Usr 15619: Rem: CLOSE #4 160 PRINT "Datos almacenados" 170 STOP Esta version trabaja solo con discos. La línea 110 abre el canal de comunicaciones con el disco. Tr-dos permite el uso de cualquier canal del 4 al 15. Es muy importante teclear las líneas tal como se muestran. La línea 110 requiere una coma como separador, ademas de una letra W al final, por write (modo escritura) mientras la línea 130 exige un pto. y coma luego de la orden PRINT# y el apostrofe como separador de elementos. La version BETA128 para cargar de nuevo los datos se parece a su procedimiento de escritura excepto que en vez de W y PRINT#, se pone R por read para solo lectura e INPUT# y las variables son separadas por pto. y coma para su correcta lectura: 200 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 210 Randomize Usr 15619: Rem: OPEN #4,"fichero",R 215 INPUT #4;n 220 FOR l=1 TO n 230 INPUT #4;a$(l);b$(l);t$(l) 235 PRINT a$(l),b$(l),t$(l) 240 NEXT l 250 Randomize Usr 15619: Rem: CLOSE #4 Marca de fin de fichero (EOF) El último programa usaba la variable N en el bucle que controla el procedimiento de lectura, pero no siempre es posible saber de antemano cuantos datos hay que leer. Un modo de salvar el inconveniente es usando una marca de "fin de fichero", pero desgraciadamente el TR-dos carece de una funcion EOF() que capture el final del fichero (pero como veremos luego, uno pude poner su propia marca muda). Al no haber EOF(), la unica forma de detectar el final de un fichero es usando una variable muda pues como ya dijimos, no hay otro modo de hacerlo. Podría ser la la palabra "FIN", o ZZZ, ó 999, o lo que uno quiera usar y que no se espere dentro del cuerpo del fichero. Ahora se elimina DIM s$(15), ya innecesaria. He aquí el programa completo desde la línea 5 para poder compararlo con las versiones anteriores: 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n, TO 3)<>"fin" AND n<50 THEN GO TO 30 80 CLS : PRINT "Almacenando los datos" 110 Randomize Usr 15619: Rem: OPEN #4,"fichero" 120 LET l=0 125 LET l=l+1 130 PRINT #4;a$(l)'b$(l)'t$(l) 140 IF a$(l, TO 3)<>"fin" AND l<50 THEN GO TO 125 150 Randomize Usr 15619: Rem: CLOSE #4 160 PRINT "Datos almacenados" 170 STOP 200 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 210 Randomize Usr 15619: Rem: OPEN #4,"fichero" 215 LET l=0 220 LET l=l+1 230 INPUT #4;a$(l);b$(l);t$(l) 235 PRINT a$(l),b$(l),t$(l) 240 IF a$(l, TO 3)<>"fin" AND l<50 THEN GO TO 220 250 Randomize Usr 15619: Rem: CLOSE #4 II - Beta128 y TR-dos: ----------------------- Por alguna razón Digital Research Ltda. no dotó de facilidades paa manejar ficheros a sus primeras interfaces Betadisco con TR-dos vrs: 1.xx - 4.xx. Solo era posible salvar matrices individuales, como ya se ha visto, en cambio con el BETA128 y su TR-dos v. 5.xx., se corrigieron dichas limitaciones. Ahora sí era posible abrir y cerrar ficheros de texto OPEN/CLOSE y leer y escribir en ellos con INPUT#, INKEY$# y PRINT#, manteniendo una sintaxis simple y estandar. La unica limitación es que con los ficheros seriales no hay como detectar el fin de fichero, a menos que uno use una marca muda o una rutina que atrape el final del fichero como un error más. A patir de este punto, trataremos exclusivamente sobre el TRDOS 5.xx, el cual posee todas las facilidades para manejar ficheros tanto secuenciales como directos. Trdos - Ficheros secuenciales En el TR-dos, el nombre del fichero es de 8 caracteres, pudiendo admitir incluso espacios. Cualquier nombre de 10 caracteres que se de como maximo será truncado a 8. Nota: Para la explicacion de los comandos del BETA128, ver el manual del TR-dos v. 5.xx. Como ejemplo vamos a crear un fichero que contenga los numeros del 1 al 100: 100 OPEN #4,"num.dat",W 120 FOR n=1 TO 100 130 PRINT #4; n 140 NEXT n 150 PRINT #4;-1 160 CLOSE #4 170 STOP La línea 150 genera la marca muda de fin de fichero. Una variante del ejemplo anterior, salvando los datos como cadenas. Podemos añadir la sigte línea al ejemplo anterior: 105 DIM n$(3) y modificamos la línea que salva los datos del sigte. modo: 130 LET n$=STR$ n: PRINT #4; n$ 150 PRINT #4;CHR$ 0 Una vez que el fichero ha sido creado y guardado en el disco, se puede recuperar o accesar cuando se desee. La sigte. rutina permite leer todo el fichero salvado hasta alcanzar la marca muda que sirve de EOF: 300 Randomize Usr 15619: Rem: OPEN #4,"num.dat",R 320 INPUT #4;n 330 IF n=-1 THEN GO TO 360 340 PRINT n 350 GO TO 320 360 PRINT "fin de fichero" 370 Randomize Usr 15619: Rem: CLOSE #4 En el caso de haber salvado los datos como cadenas, las modificaciones serán: 320 INPUT #4;n$ 330 IF n$=CHR$ 0 THEN GO TO 360 340 PRINT n$ Nota: en el caso de solo variables numericas sin ningun caracter alfabetico, la marca de fin de fichero debe de ser un número que no forme parte de la lista a salvar, en este caso '-1', para su correcta lectura. En el caso de los valores alfanuméricos, yo suelo usar CHR$ 0, pero cualquier otro valor sirve. Ahora para leer datos separadamente, como muestra el sigte. ejemplo: 1000 Randomize Usr 15619: Rem: OPEN #6,"prb.dat", W 1010 PRINT #6;"prueba"'"#2","numero";128 1020 Randomize Usr 15619: Rem: CLOSE #6 1050 Randomize Usr 15619: Rem: OPEN #6,"prb.dat", R 1060 INPUT #6; n$; m$: PRINT n$'m$ 1070 Randomize Usr 15619: Rem: CLOSE #6 1075 STOP Nota: si la cadena salvada tuviera caracteres como las comillas (") por ejemplo, la sentencia INPUT deberá incluir la isntrucción LINE para su correcta lectura. Ejemplo: 1060 INPUT #6; LINE n$ A continuacion vemos como se lee una cadena previamente salvada, mediante INKEY$ #, es decir caracter a caracter: 850 RANDOMIZE USR 15619: REM : OPEN #5,"chr.dat",R 860 LET n$=INKEY$#5 870 IF n$= CHR$ 0 THEN GO TO 885 875 IF n$<>CHR$ 13 THEN PRINT n$ 880 GO TO 860 885 RANDOMIZE USR 15619: REM : CLOSE #5 La rutina se detiene al encontrar la marca muda de EOF, o imprime si no halla un ENTER. Añadiendo datos a un fichero secuencial TR-dos está siempre en modo APPEND, así que es posible añadirle nuevos datos a un fichero previamente salvado. APPEND : 1100 REM modo append 1110 Randomize Usr 15619: Rem: OPEN #4,"num2.dat",W 1120 FOR n=100 TO 0 STEP -1 1130 PRINT #4;n 1140 NEXT n 1150 Randomize Usr 15619: Rem: CLOSE #4 Basta con volverlo a abrir otra vez en modo W, para seguir agregándole nuevos datos: 1210 Randomize Usr 15619: Rem: OPEN #5,"num2.dat",W 1220 FOR n=1 TO 100 1230 PRINT #5;n 1240 NEXT n 1245 PRINT #5;-5 1250 Randomize Usr 15619: Rem: CLOSE #5 Esta vez los datos han sido salvados con una marca muda de EOF, a diferencia de antes. Para añadir más datos ahora, habría que modificar la rutina de lectura para que reconozca la marca muda y así no la añada al fichero con los futuros datos que se pondrán ya que entonces el fichero acabará con un montón de marcas mudas en diferentes partes de la misma, cosa que no deseamos que pase. OPEN W no reinicia el fichero desde cero a menos que el nombre dado no exista. Para resetear el puntero a cero hay que eliminar todo el fichero con ERASE y luego crearlo otra vez con OPEN W. EOF - versión 1 El sigte ejemplo lee los datos hasta encontrar la marca muda en mi caso, uso CHR$ 0: 1300 REM eof 1 1310 Randomize Usr 15619: Rem: OPEN #6,"num2.dat",R 1315 LET a$="" 1320 LET m$=INKEY$#6 1330 LET EOF=(m$=CHR$ 0): IF EOF THEN GO TO 1390 1340 IF m$=CHR$ 13 THEN GO TO 1370 1350 LET a$=a$+m$ 1360 GO TO 1320 1370 LET n=VAL a$ 1380 PRINT n 1385 GO TO 1315 1390 Randomize Usr 15619: Rem: CLOSE #6 1400 STOP EOF - versión 2 En este caso en vez de usar una variable EOF, he elegido crear una FN E() por EOF(): 1410 DEF FN e(n$)=(n$=CHR$ 0) 1420 Randomize Usr 15619: Rem: OPEN #5,"num2.dat",R 1425 LET a$="" 1430 LET m$=INKEY$#5 1432 IF FN E(m$) THEN GO TO 1490 1435 IF m$ =CHR$ 13 THEN GO TO 1460 1440 LET a$=a$+m$ 1450 GO TO 1430 1460 LET n=VAL a$ 1470 PRINT n 1480 GO TO 1425 1490 Randomize Usr 15619: Rem: CLOSE #5 1500 STOP Esta subrutina contabiliza los caracteres uno a uno hasta alcanzar la marca muda: 9100 REM contabilizador 9110 LET zz=1 9120 LET EOF=(INKEY$#chn=CHR$ 0): IF EOF THEN RETURN 9140 LET zz=zz+1 9150 GO TO 9120 TRdos ofrece la posibilidad de capturar los errores de disco desde el Basic con: LET ERR = USR 15619: REM: sentencia pero este método no sirve para capturar el final del fichero, que es lo que nos interesa, ya que no es considerado un error de disco por el DOS, sino que devuelve el control al Basic, el cual da el mensaje de error "8 end of file" y se detiene inevitablemente. Debido a eso, uno está forzado a usar una marca muda o de lo contrario usar una rutina de captura de errores de Basic. TR-dos - Ficheros de acceso directo Hasta ahora hemos visto cómo leer y añadir nuevos datos a un fichero secuencial. Pasamos pues a ver como trabajar con ficheros directos o aleatorios. Para comprender mejor como opera el TR-dos, veamos el sigte ejemplo: 1800 Randomize Usr 15619: Rem: OPEN #4,"dat2.num" RND,3 1810 FOR n=1 TO 20 1820 PRINT #4;n, n*2 1830 NEXT n 1840 Randomize Usr 15619: Rem: CLOSE #4 1850 STOP 1900 Randomize Usr 15619: Rem: OPEN #4,"dat2.num" RND,3 1910 FOR n=1 TO 20 1920 INPUT #5;(n), m$: PRINT m$ 1930 NEXT n 1940 Randomize Usr 15619: Rem: CLOSE #4 1950 STOP En el ejemplo de ahora, tenemos que los valores son salvados como numeros, pero leidos como cadenas de 3 digitos (esto es indicado en la opcion RND,x al final de OPEN, donde x es la longitud máxima de la cadena a salvar dentro del registro dado) y que el puntero de datos es fijado en las mismas ordenes PRINT# e INPUT#, siendo puesto entre parentesis () en el ultimo caso, para evitar que sea confundido con un dato a leer. Es aconsejable salvar cadenas de longitud fija para mover el puntero a donde queremos, siempre, de lo contrario leeremos los datos erroneamente. El sigte ejemplo lee un fichero en modo directo: 2350 Randomize Usr 15619: Rem: OPEN #4,"num3.dat" RND,3 2360 INPUT "Registro #:";reg: IF reg=-1 THEN GO TO 2380 2370 INPUT #5;(reg), a$: PRINT a$ 2375 GO TO 2360 2380 Randomize Usr 15619: Rem: CLOSE #4 2385 STOP Un fichero previamente salvado como secuencial no puede ser accedido ni modificado de modo directo y viceversa. Veamos otro ejemplo de trabajo con ficheros de acceso directo: 3000 REM modo rnd write 3005 DIM n$(3) 3010 Randomize Usr 15619: Rem: OPEN #4,"numx.rnd" RND,3 3020 FOR n=1 TO 10 3040 LET n$=STR$ (n*2) 3050 PRINT #4;n, n$ 3060 NEXT n 3070 Randomize Usr 15619: Rem: CLOSE #4 3080 STOP 3100 REM modo rnd read 3105 DIM n$(3) 3110 Randomize Usr 15619: Rem: OPEN #4,"numx.rnd" RND,3 3120 FOR n=1 TO 10 3140 INPUT #5;(n), n$ 3150 PRINT n$ 3160 NEXT n 3170 Randomize Usr 15619: Rem: CLOSE #4 3180 STOP 3200 REM modo rnd rd/wr 3210 Randomize Usr 15619: Rem: OPEN #4,"numx.rnd" RND,3 3220 INPUT "registro #:";reg 3225 IF reg<1 THEN GO TO 3300 3226 IF reg>10 THEN INPUT "Registro inexistente intente de nuevo";v$: GO TO 3220 3240 INPUT #5;(reg), n$ 3250 INPUT "El registro ";(reg)'"contiene el valor: ";(n$)'"Desea modificarlo? ( s/n) ";v$ 3260 IF v$<>"s" AND v$<>"S" THEN GO TO 3220 3270 INPUT "Ingrese nuevo valor: ";n: IF n=0 THEN GO TO 3220 3280 PRINT #4;reg, STR$ n 3290 GO TO 3220 3300 Randomize Usr 15619: Rem: CLOSE #4 8990 STOP BETABASIC y TR-dos: TR-dos salva los numeros como cadenas que ocupan el espacio equivalente a los digitos que lo conforman, así 5, ocupa 1 caracter, mientras 22, 105, 1500 y 45000 ocupan 2, 3, 4, y 5 caracteres respectivamente. Para compactar los valores enteros hay dos funciones en el Basic Microsoft que son muy utiles para salvar numeros, y son MKI$ (make integer) y CVI (convert integer) los cuales sirven para convertir un valor entero a cadena de dos digitos y viceversa. BetaBasic ofrece sus equivalentes en CHAR$() y NUMBER(), las cuales se pueden definir del sigte modo si se carece de BetaBasic: DEF FN C$(n)=CHR$ INT (n/256)+CHR$ (n-INT (n/256)*256) ó FN M$(... DEF FN N(c$)=256*CODE c$(1)+CODE c$(2) ó FN C(... Ejemplos: 100 REM en el programa de escritura 110 DIM x$(2) 120 LET x$=CHAR$(x) 100 REM en el programa de lectura 120 LET a=NUMBER(a$) Nota: En el caso de BetaBasic 3.xx, basta un Randomize Usr 63243, para hacerlo plenamente compatible con el Beta128, y poder así usar sus comandos de disco sin problemas. Existen otras funciones en el Basic Microsoft asociadas a los ficherso directos y que son: MKS$ (make single), CVS (convert single), MKD$ (make double) y CVD (convert double), los cuales solo tienen su equivalente en la funcion FP$(floating point) del Betabasic 4. BetaBasic tambien ofrece una función EOF(n), la cual no funciona con Tr-dos, para poder capturar el EOF debe usarse en su lugar la instrucción: "ON ERROR: IF ERROR = 8 THEN ..." Para terminar este articulo ofrezco este programa originalmente publicado en el manual del Timex FDD 3000, pero convertido para correr en el TR-dos: 100 DIM a$(100) 110 LET end=20000 115 Randomize Usr 15619: Rem: OPEN #4,"addres.r" RND,100 120 INPUT "1=Leer,2=Escribir y 3=Fin";h$ 130 IF h$="2" THEN GO TO 500 135 IF h$="3" THEN GO TO 610 140 IF h$<>"1" THEN GO TO 120 150 REM lee un registro 160 INPUT "Registro numero: ";n 170 IF n>65535 OR n<1 THEN GO TO 160 180 GO SUB 1000 190 CLS 200 PRINT AT 4,3;"Nombre ";a$( TO 30) 210 PRINT AT 8,0; "Direccion ";a$(31 TO 60) 220 PRINT AT 12,2; "Telefono ";a$(61 TO 75) 230 PRINT AT 16,2; "Notas ";a$(76 TO ) 240 INPUT " para continuar";h$ 250 GO TO 120 500 REM escribe un registro 510 INPUT "Registro numero: ";n 520 IF n>65535 OR n<1 THEN GO TO 510 530 CLS 540 DIM n$(30): DIM b$(30): DIM p$(15): DIM c$(25) 550 INPUT "Nombre ";n$ 560 INPUT "Direccion ";b$ 570 INPUT "Telefono ";p$ 580 INPUT "Notas ";c$ 590 GO SUB 2000 600 GO TO 120 610 Randomize Usr 15619: Rem: CLOSE #4 615 PRINT "Terminado" 620 GO TO END 1000 REM //lee fichero// 1010 INPUT #4;(n), a$ 1930 RETURN 2000 REM //escribe fichero// 2010 PRINT #4;n, n$+b$+p$+c$ 2020 RETURN Francisco Leon zx_if1@hotmail.com