Ir al contenido


Foto

Contar Archivos y carpetas Recursivamente


  • Por favor identifícate para responder
26 respuestas en este tema

#1 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 23 febrero 2012 - 07:28

Buenas amiguitos no se molesten si posteo otra vez plis  :)....... les quiero mostrar un código que encontre para contar archivos
tomado de ecfisa
le he modificado un poquitin:



delphi
  1. Uses
  2. Windows....;
  3.  
  4. var
  5. myfiles : integer;
  6.  
  7.  
  8. procedure TotalFilesFolders(Path:string);
  9. var
  10.   SR: TSearchRec;
  11. begin
  12.   ChDir(Path);
  13.   if FindFirst ('*.*', faDirectory, SR )=0 then
  14.   repeat
  15.     if ((SR.Attr and fadirectory) = fadirectory) then
  16.     begin
  17.       if (SR.Name <> '.') and (SR.Name <> '..') then
  18.       begin
  19.         myfiles:=myfiles+1;
  20.         //Inc(TotalFolders);
  21.         TotalFilesFolders(Path + '\' + SR.Name );
  22.       end
  23.     end
  24.     else
  25.       myfiles:=myfiles+1;
  26.       //Inc(TotalFiles);
  27.   until FindNext(SR) <> 0;
  28.   FindClose( SR );
  29. end;



y lo llamo de la siguiente manera:


delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   myfiles:=0;
  4.   TotalFilesFolders('E:\');
  5.   ShowMessage(IntToStr(myfiles));
  6. end;


Funciona bien pero que le tengo que agregar al código para que me cuente los archivos y carpetas
estando estos ocultos, pues estando visibles si los cuenta pero ocultos me manda 0...


Gracias..... :)


Edito: He modificado el mensaje para colocar el link al código del amigo ecfisa de ClubDelphi.
  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 24 febrero 2012 - 03:04

Te propongo esta otra función. Cuenta todas las carpetas, subcarpetas y archivos:


delphi
  1. function FileFolderCount(Dir:String): integer;
  2. var
  3.   sr: TSearchRec;
  4. begin
  5.     Result:= 0;
  6.     if FindFirst(Dir + '\*.*', faAnyFile, sr) = 0 then
  7.     repeat
  8.       if (sr.Name <> '.') and (sr.Name <> '..') then
  9.       begin
  10.           if (sr.Attr and faDirectory)<>0 then
  11.           begin
  12.             inc(Result);
  13.             // Para añadir subcarpetas
  14.             Result:= Result + FileFolderCount(Dir + '\' + sr.Name);
  15.           end else
  16.             inc(Result);
  17.       end;
  18.     until FindNext(sr)<>0;
  19.     FindClose(sr);
  20. end;


Ejemplo de uso:

delphi
  1.   Label.Caption:= IntToStr(FileFolderCount('C:\adaptec'));



Saludos.
  • 0

#3 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 24 febrero 2012 - 08:28

Gracias amigo lo probarè...... :) :)
  • 0

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 25 febrero 2012 - 09:27

El problema de porqué no te cuenta las carpetas y archivos ocultos está en que tu solo evalúas con el atributo faDirectory. Si lees en la documentación para el parámetro Atrr son posibles los siguientes valores:

faReadOnly $00000001 Read-only files
faHidden $00000002 Hidden files
faSysFile $00000004 System files
faVolumeID $00000008 Volume ID files
faDirectory $00000010 Directory files
faArchive $00000020 Archive files
faAnyFile $0000003F Any file


Lee la ayuda sobre FindFirst. Te aclarará las dudas de cómo utilizar esta función.

Por cierto, los ejemplos mostrados contabilizan el total, si lo que buscas es contar archivos y carpetas dentro de cada subcarpeta deberás disponer de una estructura de datos llamada árbol diseñado para llevar dichos datos. Aunque claro, ahora será un poco más complicado el código.

Saludos,
  • 0

#5 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 26 febrero 2012 - 11:27


El problema de porqué no te cuenta las carpetas y archivos ocultos está en que tu solo evalúas con el atributo faDirectory...

Buena aclaración, por eso en mi ejemplo uso faAnyFile, incorporando a la cuenta todo tipo de archivos y carpetas.


...los ejemplos mostrados contabilizan el total, si lo que buscas es contar archivos y carpetas dentro de cada subcarpeta deberás disponer de una estructura de datos llamada árbol diseñado para llevar dichos datos. Aunque claro, ahora será un poco más complicado el código.

Mi ejemplo calcula los totales pero, como puedes ver, cuenta todas las carpetas y archivos incluidas las subcarpetas. Si queremos diferenciar lo que son carpatas y subcarpetas, por un lado, y archivos por otro (como lo hace el explorador al ver las propiedades de una carpeta) el código no se complica casi nada:


delphi
  1. procedure FileFolderCount2(Dir:String; var FolderCount, FileCount: integer);
  2. var
  3.   sr: TSearchRec;
  4.   nFolders, nFiles: integer;
  5. begin
  6.     FolderCount:= 0; FileCount:= 0;
  7.     if FindFirst(Dir + '\*.*', faAnyFile, sr) = 0 then
  8.     repeat
  9.       if (sr.Name <> '.') and (sr.Name <> '..') then
  10.       begin
  11.           if (sr.Attr and faDirectory)<>0 then
  12.           begin
  13.             inc(FolderCount);
  14.             // Para añadir subcarpetas
  15.             FileFolderCount2(Dir + '\' + sr.Name, nFolders, nFiles);
  16.             inc(FolderCount, nFolders);
  17.             inc(FileCount, nFiles);
  18.           end else
  19.             inc(FileCount);
  20.       end;
  21.     until FindNext(sr)<>0;
  22.     FindClose(sr);
  23. end;

Forma de uso_


delphi
  1. var
  2.   nFolders, nFiles: integer;
  3. begin
  4.   FileFolderCount2('C:\Archivos de programa', nFolders, nFiles);
  5. end;



Saludos.
  • 0

#6 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 26 febrero 2012 - 11:40

Gracias maestro escafandra, como de costumbre, código elegante y valioso. (y) (y)

Saludos
  • 0

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 26 febrero 2012 - 12:58

Mi ejemplo calcula los totales pero, como puedes ver, cuenta todas las carpetas y archivos incluidas las subcarpetas. Si queremos diferenciar lo que son carpatas y subcarpetas, por un lado, y archivos por otro (como lo hace el explorador al ver las propiedades de una carpeta) el código no se complica casi nada:


A lo que yo apunto es que si se necesita llevar la cuenta tanto de los archivos y carpetas dentro de un directorio, de forma recursiva, y sin totalizar "hacia arriba" entonces SI se requiere de una estructura de datos árbol:



delphi
  1. +----+----------+-------------+
  2. -|Raiz|Archivos: |SubCarpetas: |
  3. +----+----------+-------------+
  4.   |
  5.   |  +-------------+----------+-------------+
  6.   +--|SubCarpeta 1 |Archivos: |SubCarpetas: |
  7.   |  +-------------+----------+-------------+
  8.   |    |
  9.   |    |
  10.   |    +--- ...
  11.   |
  12.   ...
  13.   |  +-------------+----------+-------------+
  14.   +--|SubCarpeta N |Archivos: |SubCarpetas: |
  15.     +-------------+----------+-------------+



Espero que se entienda a lo que apunto.

Saludos,
  • 0

#8 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 26 febrero 2012 - 04:08

A lo que yo apunto es que si se necesita llevar la cuenta tanto de los archivos y carpetas dentro de un directorio, de forma recursiva, y sin totalizar "hacia arriba" entonces SI se requiere de una estructura de datos árbol...

...Espero que se entienda a lo que apunto.


Si, ya entiendo lo que querías decir  :D :D :D. Bueno, ese caso si es un poco mas complejo, pero no creo que sea la duda que planteaba monchito_elroro.


Saludos.
  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 26 febrero 2012 - 04:45

Bueno, ese caso si es un poco mas complejo, pero no creo que sea la duda que planteaba monchito_elroro.

Saludos.

Puede que no sea la duda de monchito_elroro pero yo nomás lo decía, por las dudas... Quien sabe, si al final también se podría o se requiera de eso (puede ser de interés para algunos; y quizá asi lo quieran).
Estuve pensando en como encararlo por ese lado pero tengo que reconocer que tengo oxidado la teoría de árboles-n. Por un momento me pensé en aprovechar alguno de las clases TTreeXXX pero no se me prende la lámpara.
La vía simple que podría pensar es directamente aprovechar un Tstrings y hacer el listado a como viene y se van apilando las llamadas. Algo como: DirectorioActual + CantArchivos + CantSubCarpetas.

Saludos,
  • 0

#10 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 26 febrero 2012 - 07:28

Buenas con todos, ante todo gracias al amigo escafranda por el còdigo, me ha ayudado mucho..... y por otro lado me gustarìa tener como segunda mano otra forma de hacerlo es por eso que quize ver si arreglaba el primer còdigo que puse, lo he intentado asì:


delphi
  1. procedure TotalFilesFolders(Path:string);
  2. var
  3.   SR: TSearchRec;
  4. begin
  5.   ChDir(Path);
  6.   if FindFirst ('*.*', faanyfile, SR )=0 then    // le he puesto anyfile
  7.   repeat
  8.     if ((SR.Attr and faanyfile) = faanyfile) then      // le he puesto anyfile
  9.     begin
  10.       if (SR.Name <> '.') and (SR.Name <> '..') then
  11.       begin
  12.         myfiles:=myfiles+1;
  13.         //Inc(TotalFolders);
  14.         TotalFilesFolders(Path + '\' + SR.Name );  // Este debe ser la resursividad, pero no
  15.                                                                           // pasa nada
  16.       end
  17.     end
  18.     else
  19.       myfiles:=myfiles+1;
  20.       //Inc(TotalFiles);
  21.   until FindNext(SR) <> 0;
  22.   FindClose( SR );
  23. end;


Pero que raro, si me funciona pero solo cuenta los archivos de la raìz (este oculto o no)

denme una manito..... :)
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 26 febrero 2012 - 09:13

Hola,

El error está justamente en cómo funciona la recursividad. Cada invocación a un procedimiento o función recursiva hace que se apile la llamada y se reserve un nuevo "pedazo" de memoria con el que va a trabajar. A medida que se van finalizando la pila va sacando elementos.
Si el procedimiento o función no regresa un valor que sea utilizado por la anterior llamada, entonces se pierde cualquier valor interno.
Fíjate que tu variable myFiles tiene ámbito solo dentro de cada llamada, y como ésta se va liberando a medida que sale de pila su valor se pierde, quedándote unicamente la cuenta realizada en el directorio raíz.

La solución es muy simple: debes hacer del procedimiento una función que regrese la cuenta y de ese modo poder recuperar y pasar el valor de una llamada a su "padre" o bien añadir un parámetro por valor al procedimiento.

Entonces con la 1ra alternativa:


delphi
  1. function TotalFilesFolders(Path: string): integer;
  2. ...
  3. result := result + TotalFilesFolders(Path + '\' + SR.Name);
  4. ...
  5. end;


Fíjate cómo se va acumulando y se llama a si misma. En cuanto llegue al último elemento y se tenga en pila los valores parciales, TotalFilesFolders se vuelve sobre sus pasos de modo que el valor que éste regresa se pasa al de la llamada de su padre. El padre incrementa su valor, y así hasta llegar a la primera llamada y así es como se llega a la cuenta final.

Con la 2da alternativa:


delphi
  1. procedure TotalFilesFolders(Path: string; var Count: integer);
  2. var MiCount: integer;
  3. ...
  4.   // incremento mi cuenta privada para esta llamada
  5.   inc(MiCount);
  6.   // ahora llamo recurvisamente...
  7.   TotalFilesFolders(Path + '\' + SR.Name; Count);
  8.   // ahora que volví a mis pasos en Count se la cantidad de la llamada de mi hijo
  9.   // entonces ahora se cuanto debo contar yo: Count = Count + MiCount
  10.   inc(Count,MiCount);
  11. ...
  12. end;


Con todo respeto, te hace falta repasar un poco más el concepto de recursividad.

Saludos,
  • 0

#12 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 27 febrero 2012 - 01:22

En realidad el diseño recursivo no es muy bueno, pero la cuenta si la lleva pues usa una variable global: myfiles.

No funciona porque no terminaste de entender FindFirst y FindNext.
Fíjate como quedaría el código que quieres modificar:


delphi
  1. procedure TotalFilesFolders(Path:string);
  2. var
  3.   SR: TSearchRec;
  4. begin
  5.   ChDir(Path);
  6.   if FindFirst ('*.*', faanyfile, SR )=0 then    // le he puesto anyfile
  7.   repeat
  8.     if ((SR.Attr and fadirectory) = fadirectory) then
  9.     begin
  10.       if (SR.Name <> '.') and (SR.Name <> '..') then
  11.       begin
  12.         myfiles:=myfiles+1;
  13.         //Inc(TotalFolders);
  14.         TotalFilesFolders(Path + '\' + SR.Name ); 
  15.       end
  16.     end
  17.     else
  18.       myfiles:=myfiles+1;
  19.       //Inc(TotalFiles);
  20.   until FindNext(SR) <> 0;
  21.   FindClose( SR );
  22. end;


De esta forma funciona, pero yo devolvería el valor en la función en lugar de usar una variable global.


Saludos.
  • 0

#13 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 27 febrero 2012 - 09:47

Puede que no sea la duda de monchito_elroro pero yo nomás lo decía, por las dudas... Quien sabe, si al final también se podría o se requiera de eso (puede ser de interés para algunos; y quizá asi lo quieran).
Estuve pensando en como encararlo por ese lado pero tengo que reconocer que tengo oxidado la teoría de árboles-n. Por un momento me pensé en aprovechar alguno de las clases TTreeXXX pero no se me prende la lámpara...


Bueno, por si las dudas, he desarrollado un pequeño ejemplo basado en el código FileFolderCount2 en el que incorporo una cuenta por parciales en un TTreView complicando muy poco el código. De esta forma puede que las necesidades queden cubiertas:



delphi
  1. procedure FileFolderCount3(Dir:String; var FolderCount, FileCount: integer; var TreeView: TTreeView; Node: pointer = nil);
  2. var
  3.   sr: TSearchRec;
  4.   nFolders, nFiles, lFolders, lFiles: integer;
  5. begin
  6.     FolderCount:= 0; FileCount:= 0;
  7.     lFolders:= 0; lFiles:= 0;
  8.     Node:= TreeView.Items.AddChild(Node, ExtractFileName(Dir));
  9.     if FindFirst(Dir + '\*.*', faAnyFile, sr) = 0 then
  10.     repeat
  11.       if (sr.Name <> '.') and (sr.Name <> '..') then
  12.       begin
  13.           if (sr.Attr and faDirectory)<>0 then
  14.           begin
  15.             inc(FolderCount); inc(lFolders);
  16.             // Para añadir subcarpetas
  17.             FileFolderCount3(Dir + '\' + sr.Name, nFolders, nFiles, TreeView, Node);
  18.             inc(FolderCount, nFolders);
  19.             inc(FileCount, nFiles);
  20.           end else
  21.           begin
  22.             inc(FileCount);  inc(lFiles);
  23.           end;
  24.       end;
  25.     until FindNext(sr)<>0;
  26.     FindClose(sr);
  27.     TTreeNode(Node).Text:= TTreeNode(Node).Text+' ['+IntToStr(lFolders)+' Carpetas y '+IntToStr(lFiles)+' Archivos]';
  28. end;



Forma de uso:


delphi
  1. // El formulario debe contener un TreeView o crearse previamente.
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var
  4.   Files, Folders: integer;  // Darán la cuenta total.
  5. begin
  6.   FileFolderCount3('D:\TC4', Folders, Files, TreeView1);
  7. end;




Saludos.
  • 0

#14 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 27 febrero 2012 - 10:57

¡Me rindo ante tus pies escafandra!
No se me hubiera ocurrido de esa forma, estuve divagando en como hacer un buen árbol y hacer la cuenta hacia atrás. El bosque no me dejaba ver el árbol.

Vaya si que es simple el código. Y yo que me mataba tratando de pensar una alternativa con un TAD árbol propio diseñado para el ejemplo.

Saludos,
  • 0

#15 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 27 febrero 2012 - 11:02

...Y yo que me mataba tratando de pensar una alternativa con un TAD árbol propio diseñado para el ejemplo...


Bueno, crear un árbol propio es un excelente ejercicio pero eso si que aumentaría mucho el código, es por eso que eché mano del tan conocido TTreeView. De hecho la idea la propusiste tu mismo...  :)


Saludos.
  • 0

#16 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 27 febrero 2012 - 11:20

Bueno, crear un árbol propio es un excelente ejercicio pero eso si que aumentaría mucho el código, es por eso que eché mano del tan conocido TTreeView. De hecho la idea la propusiste tu mismo...  :)

Saludos.

Pues si, después de darme por vencido al tratar de recordar la teoría de árboles (y más sabiendo que yo solamente trabajé, o mejor dicho practiqué en código, con árboles binarios) y llevarlo al concepto genérico de árboles-n es que me decía que aprovechara el TTreeView o algún otro.
La cosa es ya mi cabeza no daba más, era tarde y no había manera de que encendiera siquiera un foquito de 5W. :D

En lo que me quedé pensando es el peligro de miles de llamadas a la pila. ¿Será práctico implementar recursividad para cuando tenemos una millonada o más de archivos? El punto es que creo que no hay otro modo de hacer una exploración por un directorio sin recursión :s

Saludos,
PD: Creo que no me vendría mal leerme algo de árboles... para no perder la costumbre de ir recordando concepto que hace tiempo que los tengo oxidados.... incluso, entre ellos, un poquito de recursividad
  • 0

#17 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 28 febrero 2012 - 12:39

Gracias lo probarè..... :) :)
  • 0

#18 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 04 marzo 2012 - 03:31

Una consulta amiguitos, si en caso quiero ver estos resultados plasmados en un progressbar, es posible hacerlo con estos procedures y funciones que me han compartido....???

:undecided:
  • 0

#19 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 04 marzo 2012 - 06:41

Colocar un ProgressBar es complicado pues no sabemos de antemano cuantas carpatas y archivos se van a analizar. Podríamos calcularlo, pero eso puede consumir casi tanto tiempo como la función que queremos monitorizar con el ProgressBar (fíjate como falla el mismo Windows en sus cálculos)...

Tienes la opción de colocar una barra de desplazamiento sin fin. Si te interesa lee esto.


Saludos.


  • 0

#20 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 06 marzo 2012 - 01:06

OK gracias amigo..... otra consultita, acabo de probar la modificaciòn que le hiciste al procedure que les enviè...que es este:

En realidad el diseño recursivo no es muy bueno, pero la cuenta si la lleva pues usa una variable global: myfiles.

No funciona porque no terminaste de entender FindFirst y FindNext.
Fíjate como quedaría el código que quieres modificar:


delphi
  1. procedure TotalFilesFolders(Path:string);
  2. var
  3.   SR: TSearchRec;
  4. begin
  5.   ChDir(Path);
  6.   if FindFirst ('*.*', faanyfile, SR )=0 then    // le he puesto anyfile
  7.   repeat
  8.     if ((SR.Attr and fadirectory) = fadirectory) then
  9.     begin
  10.       if (SR.Name <> '.') and (SR.Name <> '..') then
  11.       begin
  12.         myfiles:=myfiles+1;
  13.         //Inc(TotalFolders);
  14.         TotalFilesFolders(Path + '\' + SR.Name ); 
  15.       end
  16.     end
  17.     else
  18.       myfiles:=myfiles+1;
  19.       //Inc(TotalFiles);
  20.   until FindNext(SR) <> 0;
  21.   FindClose( SR );
  22. end;


De esta forma funciona, pero yo devolvería el valor en la función en lugar de usar una variable global.


Saludos.


Lo acabo de probar pero al realizar la llamada se cuelga todo el system..... he revisado en el taskmanager y cuado lo ejecuto como que empieza a consumir toda la ram....a que se deberà......???  :undecided:
  • 0




IP.Board spam blocked by CleanTalk.