TAD Lista Ordenada
Ejemplo de uso - Explorador de directorios
El siguiente programa muestra un listado con el nombre, fecha y tamaño de todos los ficheros consistentes con un filtro que pertenecen a un determinado directorio y a todos los sus subdirectorios, y permite ordenar esa lista de acuerdo a distintos criterios (nombre, tamaño y fecha).
El programa pide el directorio inicial y el filtro. El filtro se proporciona con la misma sintaxis que el comando dir de MS-DOS. Para obtener la información sobre los ficheros se utilizan subprogramas y tipos de datos pertenecientes a la unidad dos de Turbo-Pascal, consulte la ayuda de Turbo-Pascal si desea conocer la forma de usarlos.
Un directorio consta de una lista de archivos, los cuales pueden ser a su vez directorios o ficheros. Por lo tanto, en el programa un directorio se representará mediante una lista de elementos que son variables dinámicas de tipo TArchivo, el cual es un registro que contiene campos para almacenar la información asociada a un fichero (nombre, fecha y tamaño) y a un directorio (nombre y lista de archivos).
Ya que una directorio puede contener subdirectorios, realmente la estructura representada se corresponde más con un arbol que con un lista. Debido a ello, los subprogramas LeeDirectorio, EscribeDirectorio, DestruyeDirectorio y CambiaOrden se utilizan de manera recursiva.
Al utilizar el programa se debe tener cuidado ya que es posible que al proporcionar un directorio o filtro demasiado genérico (por ejemplo, directorio "c:" y filtro "*.*") el número de ficheros sea tan grande que se agote la memoria disponible.
Puede descargar una copia del programa pulsando aquí.
{$F+} program ejemplo2(input,output); { Se puede elegir entre cualquiera de las dos implementaciones, lista1 o lista2, el resultado deberia ser el mismo. } uses lista2, dos, crt; type TCadena = string[200]; { Utilizaremos la palabra Archivo para referirnos a un fichero o a un directorio (un contenedor de archivos) } TTipoArchivo = (aFichero,aDirectorio); { Los ficheros se caraterizan por su nombre, fecha y tamaño, y los directorios por su nombre y la lista de archivos que pertenecen a él } TArchivo = record Nombre : string[12]; case Tipo: TTipoArchivo of aFichero : (Fecha: LongInt; Tamano: LongInt); aDirectorio : (Lista: TListaOrd) end; PArchivo = ^TArchivo; { Al comparar un fichero con un directorio, se ha elegido que el directorio sea siempre mayor. Al comparar dos directorios en la ordenación por fecha o tamaño, el resultado es, en realidad, el de la comparación por nombre. } function OrdenNombre(Elem1,Elem2: pointer) : boolean; begin if PArchivo(Elem1)^.Tipo <> PArchivo(Elem2)^.Tipo then OrdenNombre := PArchivo(Elem1)^.Tipo <= PArchivo(Elem2)^.Tipo else OrdenNombre := PArchivo(Elem1)^.Nombre <= PArchivo(Elem2)^.Nombre end; function OrdenFecha(Elem1,Elem2: pointer) : boolean; begin if PArchivo(Elem1)^.Tipo <> PArchivo(Elem2)^.Tipo then OrdenFecha := PArchivo(Elem1)^.Tipo <= PArchivo(Elem2)^.Tipo else if PArchivo(Elem1)^.Tipo = aDirectorio then OrdenFecha := PArchivo(Elem1)^.Nombre <= PArchivo(Elem2)^.Nombre else OrdenFecha := PArchivo(Elem1)^.Fecha <= PArchivo(Elem2)^.Fecha end; function OrdenTamano(Elem1,Elem2: pointer) : boolean; begin if PArchivo(Elem1)^.Tipo <> PArchivo(Elem2)^.Tipo then OrdenTamano := PArchivo(Elem1)^.Tipo <= PArchivo(Elem2)^.Tipo else if PArchivo(Elem1)^.Tipo = aDirectorio then OrdenTamano := PArchivo(Elem1)^.Nombre <= PArchivo(Elem2)^.Nombre else OrdenTamano := PArchivo(Elem1)^.Tamano <= PArchivo(Elem2)^.Tamano end; procedure EscribeArchivo(Archivo: TArchivo; Ultimo: boolean); { Escribe la información asociada a un archivo (nombre si es un directorio, nombre, tamaño y fecha si es un fichero). Ultimo es cierto si el archivo es el último del directorio (se utiliza para mejorar la presentación). } var DiaHora : DateTime; { tipo de datos definido en la unidad dos } Ch : char; begin if Ultimo then write(output,#192) else write(output,#195); if Archivo.Tipo = aDirectorio then writeln(output,#196,'[',Archivo.Nombre,']') else begin { Se ha ha elegido un formato basado en columnas } write(output,#196,' ',Archivo.Nombre,' ':14-Length(Archivo.Nombre)); write(output,Archivo.Tamano:12,' bytes '); { UnpackTime esta definido en la unidad dos } UnpackTime(Archivo.Fecha,DiaHora); writeln(output,DiaHora.Day:2,'/',DiaHora.Month:2,'/',DiaHora.Year,' ', DiaHora.Hour:2,':',DiaHora.Min:2,':',DiaHora.Sec:2) end end; procedure EscribeDirectorio(L: TListaOrd; Espaciado: TCadena; var Lin: integer); { Escribe la lista de archivos (almacenada en L) asociada a un directorio. Si entre los archivos existen directorios, tambien se escriben utilizando recursividad. Espaciado y Lin se utilizan para mejorar la presentación. } var Archivo : PArchivo; Ch : char; begin { Se recorre la lista } IrAInicio(L); Archivo := PArchivo(ElemActual(L)); while Archivo <> nil do begin Lin := Lin+1; IrASiguiente(L); { Se escribe el archivo } write(output,Espaciado); EscribeArchivo(Archivo^,ElemActual(L) = nil); { Se hace una pausa cada 20 lineas } if Lin mod 20 = 19 then Ch := ReadKey; { Si es un directorio, se escribe su contenido a continuación } if Archivo^.Tipo = aDirectorio then if ElemActual(L) = nil then EscribeDirectorio(Archivo^.Lista,Espaciado+' ',Lin) else EscribeDirectorio(Archivo^.Lista,Espaciado+#179+' ',Lin); Archivo := PArchivo(ElemActual(L)) end end; procedure LeeDirectorio(var L: TListaOrd; Dir,Filtro: TCadena; Orden: TFuncComp); { Este procedimiento inicializa la lista L y almacena en ella todos los ficheros que pertenecen al directorio cuya ruta esta dada por Dir y concuerdan con el filtro Filtro, junto con todos los subdirectorios de Dir que contengan al menos un fichero que concuerde con el filtro. El orden de la lista viene dado por la funcion Orden. } var Fich : SearchRec; { El tipo SearchRec se define en la unidad dos } Archivo : PArchivo; begin Inicializar(L,Orden); { Lectura de ficheros - Los subprogramas FindFirst y FindNext, y su modo de uso se definen en la unidad dos } FindFirst(Dir+'\'+Filtro,Archive,Fich); while DosError = 0 do begin new(Archivo); Archivo^.Nombre := Fich.Name; Archivo^.Tipo := aFichero; Archivo^.Fecha := Fich.Time; Archivo^.Tamano := Fich.Size; Insertar(L,Archivo); FindNext(Fich) end; { Lectura de subdirectorios } FindFirst(Dir+'\*.*',Directory,Fich); while DosError = 0 do begin { Los subdirectorios "." y ".." representan al directorio actual y al directorio padre, respectivamente. No son directorios reales, si se procesaran darian lugar a una recursión infinita } if (Fich.Attr = Directory) and (Fich.Name <> '.') and (Fich.Name <> '..') then begin new(Archivo); Archivo^.Nombre := Fich.Name; Archivo^.Tipo := aDirectorio; { Parecería lógico llamar ahora recursivamente a LeeDirectorio para rellenar Archivo^.Lista con los ficheros del subdirectorio, pero debido a que FindFirst y FindNext estan definidos de forma que sólo se puede leer un directorio a la vez, esas llamadas se posponen hasta haber terminado de leer el directorio actual } Insertar(L,Archivo) end; FindNext(Fich) end; { Lectura recursiva de subdirectorios } IrAInicio(L); Archivo := PArchivo(ElemActual(L)); while Archivo <> nil do begin if Archivo^.Tipo = aDirectorio then begin { Llamada recursiva } LeeDirectorio(Archivo^.Lista,Dir+'\'+Archivo^.Nombre,Filtro,Orden); { Si el directorio esta vacio, lo borramos } IrAInicio(Archivo^.Lista); if ElemActual(Archivo^.Lista) = nil then Borrar(L) { Nota: Al borrar se pasa al elemento siguiente } else IrASiguiente(L) end else IrASiguiente(L); Archivo := PArchivo(ElemActual(L)) end end; procedure DestruyeDirectorio(var L: TListaOrd); { Destruye las variables dinámicas creadas para almacenar los datos de la lista. Ya que dentro de esos datos puede haber otras listas (si representan a subdirectorios), se realizarán llamadas recursivas para destruirlas también } var Archivo : PArchivo; begin IrAInicio(L); Archivo := PArchivo(ElemActual(L)); while Archivo <> nil do begin if Archivo^.Tipo = aDirectorio then DestruyeDirectorio(Archivo^.Lista); dispose(Archivo); IrASiguiente(L); Archivo := PArchivo(ElemActual(L)) end; { Por último, se borra la memoria interna usada por la lista } Destruir(L); end; procedure CambiaOrden(var L: TListaOrd; Orden: TFuncComp); { Se cambia el orden de la lista. Debido a que pueden existir datos que contengan otras listas (en el caso de subdirectorios), se llama recursivamente a CambiaOrden sobre ellas } var Archivo : PArchivo; begin Reordenar(L,Orden); IrAInicio(L); Archivo := PArchivo(ElemActual(L)); while Archivo <> nil do begin if Archivo^.Tipo = aDirectorio then CambiaOrden(Archivo^.Lista,Orden); IrASiguiente(L); Archivo := PArchivo(ElemActual(L)) end end; var Dir,Filtro : TCadena; Lista : TListaOrd; Opcion : char; Lin : integer; begin ClrScr; { Borrado de pantalla } { Se pide el directorio inicial y el filtro que se aplica } write(output,'Directorio inicial: '); readln(input,Dir); write(output,'Filtro: '); readln(input,Filtro); { Se crea la lista de archivos: Ficheros que cumplan el filtro y subdirectorios que contengan algún fichero que cumpla el filtro. Esta lista no va a cambiar (excepto por la ordenación) en el resto de la ejecución del programa. } LeeDirectorio(Lista,Dir,Filtro,OrdenNombre); repeat writeln(output,'[',Dir,']'); Lin := 0; EscribeDirectorio(Lista,'',Lin); writeln(output,'[S]: Salir. Reordenar por [N]: Nombre [F]: Fecha [T]: Tamaño'); write(output,'Escoja opcion: '); Opcion := ReadKey; { ReadKey está definida en la unidad crt } case Opcion of 'n','N': CambiaOrden(Lista,OrdenNombre); 'f','F': CambiaOrden(Lista,OrdenFecha); 't','T': CambiaOrden(Lista,OrdenTamano) end until Opcion in ['s','S']; { Se destruye la lista y las posibles sublistas } DestruyeDirectorio(Lista) end.