REPUBLICA BOLIVARIANA DE VENEZUELA. UNIVERSIDAD DEL ZULIA FACULTAD DE INGENIERÍA DIVISIÓN DE POSTGRADO COMPUTACIÓN APLICADA TÉCNICAS DE PROGRAMACIÓN: TAREA 3 Profesor: Ing. Daniel Finol Nombre: José V. Gómez D. CI: 10.084.584. MARACAIBO, MAYO DEL 2006. I. Tarea 3: Crear un programa que mejore al de la tarea 2, añadiendo las siguientes modificaciones: 1. No debe prefijarse un límite al número de elementos que pueden incluirse en la (o las) pila (s). 2. Dentro de las comillas se debe llevar una cuenta aparte de la concordancia de las llaves ("llaves" = llaves, paréntesis y corchetes): es decir, las llaves dentro de comillas no afectan la correspondencia de las llaves fuera de ellas; pero dentro de las comillas sí debe haber correspondencia entre las llaves que cierran y abren. Al salir de las comillas se debe continuar con la correspondencia que se llevaba de las llaves externas. 3. El texto se leerá de un archivo cuyo nombre proveerá el usuario como primer argumento al programa. En caso de no existir argumentos del programa el texto se leerá de la entrada estándar. Pueden revisar en el libro las funciones de la biblioteca estándar para manejo de archivos. Algunas de las funciones que pueden usar son: fopen, fclose, fgetc, fputc. 4. La salida se imprimirá a un archivo cuyo nombre proveerá el usuario como segundo argumento al programa. En caso de no existir argumentos del programa el texto se imprimirá a la salida estándar. 5. Tampoco se debe limitar el tamaño de la línea (una manera de hacer esto es leer un carácter a la vez y procesarlo, en vez de leer línea por línea). 6. El programa principal sólo debe tener acceso a la pila a través de las funciones correspondientes; es decir, la pila debe estar implementada de tal manera que quien (el programa o la función) la use no tenga acceso a la estructura de datos subyacente y así, no pueda, por ejemplo, modificar erróneamente el puntador al tope y así dañar la estructura al estropear la secuencia. Además esto permite que se modifique (de ser necesario) la implementación (estructura) interna de la pila sin que esto afecte el programa o función que la usa; y así este último no tendría que modificarse. 7. Siempre debe indicarse cuál fue el error (si lo hubo). Por ejemplo: qué llave faltó cerrar. 8. Cuando falte una llave por cerrar debe indicarse cuál es la posición de esa llave; además de cuál llave es. 9. El programa imprimirá cada línea precedida de su número seguido de dos puntos (:) y dos tabuladores. En estos ejemplos: 1. Estas son externas: { ( [ "aquí estamos dentro de comillas [ { abcd } ] termina" más texto ] más aún )}. 2. ({Aquí sí hay error(es) "abre ( otro { " }).... 3. Aquí hay más texto (etcétera { más todavía "este corchete [ se debe imprimir y ya no ignorar" ] } ). 4. Aquí hay más texto (etcétera { más todavía "este corchete [ se debe imprimir] y ya no ignorar" ] } ). 5. Aquí hay más texto (etcétera { más todavía este corchete [ se debe "imprimir] y ya no ignorar" ] } ). 6. Esta frase: "abre paréntesis (" abre corchete [, abre { etcétera } ] ). Los errores serían: 1. 2. 3. 4. 5. 6. Aquí no hay error. No se cierra la llave dentro de las comillas. Hay que cerrar el corchete dentro de las comillas. Hay un corchete cerrado de más (fuera de las comillas. Hay un corchete cerrado de más (dentro de las comillas). No se cierra el paréntesis dentro de las comillas. Consideraciones: Debe asegurarse de liberar cada segmento de memoria reservado dinámicamente que ya no se necesite. Al abrir el archivo debe verificar si realmente se abrió, y, en caso contrario, informar al usuario y terminar. El número de columna es el número de caracter de la línea donde se encontró el error. Aunque los ejemplos son de una línea el programa debe verificar el texto completo; no línea por línea; es decir, es correcto que un paréntesis abra en una línea y cierre en cualquier línea posterior. Explicación del Código fuente: En la función main se introducen los argumentos (código ejecutable, archivo de entrada, archivo de salida) para leer del archivo de entrada carácter por carácter su contenido ( si no existe lo lee de la entrada estándar) y la salida la coloca en el archivo de salida ( si no existe lo escribe en la salida estándar). En línea de instrucción : FILE *arch_ent,*arch_sal,*fopen(); arch_ent es un apuntador a FILE y la función fopen() devuelve un apuntador a FILE de la librería estándar . Si no se puede abrir el archivo de entrada se envía un error a la salida estándar. Dentro de la función principal -main( )- tenemos: * Si se cumple argc = =1: Se lee de la entrada estándar y se escribe en la salida estándar. Se usa una pila dinámica la cual utilizamos para llevar el control de abrir y cerrar llaves -“(“, “[“, “{“ , “}”, “]”, “)” – que están fuera de las comillas . Esta pila se va llenando al obtener el carácter lee ido de la entrada estándar y llamar función push, luego al leer un carácter de las llaves nombradas se vacía la pila al llamar la función pop (si se encuentra con un tipo de llave es cuando se introduce o se saca de la pila). Esto se va canalizando a través de un switch-case. El primer ciclo while ayuda a verificar e imprimir carácter por carácter del texto hasta que se consigue un error (llaves no cerradas correctamente) . Se verifica si se abrió una comilla para no tomarlas en cuenta en la validación de cierre de los caracteres “(“, “[“, “{“ , “}”, “]”, “)” externos con : if (comi==2) comi=0; if (line[i]=='"' || comi==1 || comi==2){ /*se cumple al estar abierto una comilla*/ if (line[i]=='"' & comi==1) comi=2; . . . if (line[i]=='"' & comi==0) comi=1; Sino se cumple - if (line[i]=='"' & comi==0) – se lleva el control de las llaves externas. Sino no se abrió una comillas o se cerro una comilla abierta se hace la validación de paréntesis, corchetes y llaves - “(“, “[“,y“{“ - abiertas para ver si están cerradas correctamente . Esto se hace al conseguir un paréntesis, corchete y llave abierta y sacando del tope de la pila (con la función pop) el paréntesis, corchete o llave para compararlos y verificar así que este cerrado correctamente. Si no esta cerrado correctamente se dice cual es el error y en que línea y columna esta el mismo (esto se muestra a la salida estándar. Este conteo, de las líneas y columnas, se hace a través de: if ((c=='\n') && (i==0)){ printf("%d",linea+1); printf(":\t\t"); } if (c!='\n'){ i++; if (i==1){ printf("%d",linea+1); printf(":\t\t"); } printf("%c",c); } else{ i=0; linea++; printf("\n"); } Al cumplirse - if (line[i]=='"' & comi==0) – se lleva el control de las llaves internas y se termina de examinar al conseguir el error. Para llevar el control de las llaves internas (dentro de las comillas) se hace lo mismo que se hizo para las llaves externas. Usando otra pila dinámica. Todo esto se hace dentro del primer ciclo while ( las dos pilas se llena y vacían aquí para llevar el control interno y externo de las llaves) .Esto a su vez esta adentro de la comparación afirmativa de argc = =1. * Si argc = =2 o argc = =3: Se lee el archivo de entrada.txt y escribe: en el archivo de salidat.txt si existe (argc==3, es decir, 3 argumentos) o en la salida estándar si argc==2 (2 argumentos: código ejecutable y entrada.txt). Para esto se hace el mismo procedimiento del primer while, utilizando un segundo while y las instrucciones correspondientes al manejo de “Archivos”. Aquí también se lleva el control de las llaves externas e internas en forma separada. Como se explico en el primer while. La instrucción: if ((arch_ent=fopen(*++argv,"r"))==NULL){ Abre el archivo de entrada.txt en modo de lectura y verifica si se puede abrir. La instrucción: arch_sal=fopen(*++argv,"w"); Abre archivo de salidad.txt en modo de escritura. Con las instrucciones: fprintf(arch_sal,":\t\t"); putc(c,arch_sal); Se imprime en el archive de salida.txt . Todo esto se lleva en el segundo while como se explico anteriormente. CODIGO FUENTE DEL PROGRAMA: TAREA3-JoseG.c