Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Listar carpetas y subcarpetas (https://www.clubdelphi.com/foros/showthread.php?t=84683)

darkmir 21-11-2013 23:26:39

Listar carpetas y subcarpetas
 
Hola a todos, trato de listar archivos, carpetas y subcarpetas en un ListBox, pero solo llego a listar archivos y carpetas de la primera capa, osea no me lista los archivos que estan dentro de otras carpetas:

mi codigo es el siguiente:

Cita:

#include <io.h> //las librerias que uso para listar los archivos
#include <fstream.h>

void listar()
{
struct _finddata_t arch;
long result;

AnsiString files = "E\\*.*";

if ((result = _findfirst(files.c_str(), &arch)) == -1L ) // si hay error que no busque nada
{
ListBox1->Items->Add(" No hay archivos para Desocultar ");
}
else
{
do
{
ListBox1->Items->Add(files);
}
while ( _findnext(result, &arch) == 0 ); // sigue buscando mientras no haya error
_findclose(result);
}

}
trate de ponerle un condicional, diciendole que si era carpeta que liste sus archivos y carpetas dentro de ella, pero solo serviria como segunda capa, si hay mas subcarpetas tendria que ponerle denuevo otra condicional y otra y otra.... estoy trantando de hacer un codigo el cual me liste las subcarpetas indefinidas y no solo la primera o segunda capa, ya que no sabremos cuantas subcarpetas pueden estar dentro de otra subcarpeta....

agradesco de antemano sus respueestas.

Saludos.

ecfisa 22-11-2013 05:02:39

Hola darkmir.

La forma mas simple de concebir el algoritmo es de forma recursiva:
Código:

#include <io.h>
#include <dir.h>

void ListFiles(char *Dir, TListBox *LB)
{
  struct _finddata_t fdt;
  long hFile;
  char tmp[MAX_PATH];

  chdir(Dir);
  fdt.attrib = _A_SUBDIR;
  if( (hFile = _findfirst("*.*", &fdt) ) != -1) {
    do {
      if (fdt.attrib == _A_SUBDIR) {
        if (strcmp(fdt.name,".")!= 0 && strcmp(fdt.name,"..") != 0) {
          strcpy(tmp, Dir);
          strcat(tmp, "\\");
          strcat(tmp, fdt.name);
          ListFiles(tmp, LB);
        }
      }
      else
        LB->Items->Add(String(fdt.name));
    } while (_findnext(hFile, &fdt) == 0);
    _findclose(hFile);
  };
}

Sin embargo, si estas usando C++ Builder, es mas simple hacer:
Código:

void ListFiles(AnsiString Dir, TListBox *LB)
{
  TSearchRec sr;

  ChDir(Dir);
  if (FindFirst("*.*", faDirectory, sr) == 0) {
    do {
      if (sr.Attr == faDirectory) {
        if (sr.Name != "." && sr.Name != "..")
          ListFiles(Dir + "\\" + sr.Name, LB);
      } else
        LB->Items->Add(sr.Name);
    } while (FindNext(sr) == 0);
    FindClose(sr);
  }
}

Ejemplo de uso para ambas funciones:
Código:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ListFiles("C:\\UNA_CARPETA", ListBox1);
}

Saludos :)

darkmir 22-11-2013 05:56:59

Gracias, me funciono bien.
 
Gracias ecfisa,

el codigo hace justo lo que necesitaba, mi unico inconveniente es que ambos codigos me leen sin problemas cuando pongo una unidad (E:\\), pero cuando pongo (E:\\LOGS\\) osea una unidad + "cualquier carpeta", me manda un error y no termina mi programa, lo solucione con un try{}catch(...){} cuando llamo la función, pero tratare de arreglarlo sin necesidad de un try-catch, veo que podria ser algun problema al momento de leer alguna carpeta especifica dentro de una unidad y como hay otras carpetas genera ese error, pero en fin es lo de menos.

Te agradesco por la ayuda.^\||/

ecfisa 22-11-2013 15:10:19

Hola darkmir.

La verdad no me explico ese comportamiento...

Probé ambos códigos usando sólo la unidad, una carpeta, una carpeta y subcarpeta y funciona correctamente, como ejemplo:
Código:

  ListFiles("C:\\WINDOWS\\FONTS\\", ListBox1);
¿ Cuál es exactamente el mensaje de error que te muestra ?

Saludos :)

darkmir 22-11-2013 19:30:02

Hola ecfisa,
bueno , en si, no me manda ningún mensaje de error, pero se cuelga y se cierra el programa, me he dado cuenta que es con carpetas que tienen bastantes archivos, por ejemplo lo hice con "F:\\DOCUMENTOS\\" y ahi sale se cuelga, tengo algo de 400 gb de contenido, lo depure para ver el error y sale esto:



pero lo arregle con try catch, ahora no me sale error, pero me parece que fuera por los atributos porque solo me pasa con esa carpeta y con la carpeta "C:\\windows\\", con otras carpetas corre sin problemas.

Otra consulta, quise hacerlo en uno solo la funcion y la llamada pero no me sale exactamente como los anteriores codigos, como por ejemplo,unir en uno solo este código el cual me proporcionaste:


Código:

void ListFiles(char *Dir, TListBox *LB)
{
  struct _finddata_t fdt;
  long hFile;
  char tmp[MAX_PATH];

  chdir(Dir);
  fdt.attrib = _A_SUBDIR;
  if( (hFile = _findfirst("*.*", &fdt) ) != -1) {
    do {
      if (fdt.attrib == _A_SUBDIR) {
        if (strcmp(fdt.name,".")!= 0 && strcmp(fdt.name,"..") != 0) {
          strcpy(tmp, Dir);
          strcat(tmp, "\\");
          strcat(tmp, fdt.name);
          ListFiles(tmp, LB);
        }
      }
      else
        LB->Items->Add(String(fdt.name));
    } while (_findnext(hFile, &fdt) == 0);
    _findclose(hFile);
  };
}
/////////////////////////////////////////////////////////
void __fastcall TForm1::Button1Click(TObject *Sender)
{

        ListFiles("F:\\", ListBox1);



}
//---------------------------------------------------------------------------

lo queria unir así:

Código:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  char *Dir = "F:\\";
  struct _finddata_t fdt;
  long hFile;
  char tmp[MAX_PATH];

  chdir(Dir);
  fdt.attrib = _A_SUBDIR;
  if( (hFile = _findfirst("*.*", &fdt) ) != -1) {
    do {
      if (fdt.attrib == _A_SUBDIR) {
        if (strcmp(fdt.name,".")!= 0 && strcmp(fdt.name,"..") != 0) {
          strcpy(tmp, Dir);
          strcat(tmp, "\\");
          strcat(tmp, fdt.name);
        // ListFiles(tmp, LB);
        }
      }
      else
        ListBox1->Items->Add(fdt.name);    // para escoger que listar, archivos o carpetas.
    } while (_findnext(hFile, &fdt) == 0);
    _findclose(hFile);
  };
}

pero no me muestra nada, bueno lo queria unir, para separar directorios de archivos, y al final escoger cual listar, o bien directorios o bien archivos, algo así:

ListBox1->Items->Add(fdt.name); // archivos
ListBox1->Items->Add(Dir); // Directorios.

si puedes ayudarme seria estupendo, igual, si me sale el código, lo estare comentando por acá.

Gracias y Saludos. ^\||/

ecfisa 22-11-2013 20:12:37

Hola darkmir.

Tratándose de tantos directorios y archivos, seguramente la profundidad (cantidad de carpetas/subcarpetas hasta el nodo) sea muy grande y por tanto las llamadas recursivas del algoritmo agoten la memoria destinada a la pila.

Al usar try/catch evitas que se lance la excepción, pero lamentablemente no va a cumplir el cometido. En este caso creo que hay que descartar la solución recursiva...


Saludos :)

darkmir 23-11-2013 05:50:25

Modificacion codigo.
 
Hola ecfisa,

estoy amoldando el codigo, pero me lanza un error en una parte, este es el codigo modificado:

Código:

  char *Dir = "F:\\" ;
  struct _finddata_t arch;
  long hFile;
  char tmp[MAX_PATH];

  chdir(Dir); //cambio al directorio actual
  arch.attrib = _A_SUBDIR;  //define si el atributo es un directorio
  if( (hFile = _findfirst("*.*", &arch) ) != -1)
    {
      do{
          if (arch.attrib == _A_SUBDIR)
            {
              if (strcmp(arch.name,".")!= 0 && strcmp(arch.name,"..") != 0)
              {
              strcpy(tmp, Dir);
              strcat(tmp, "\\");
              strcat(tmp, arch.name);
              }
            }
          else
            ListBox1->Items->Add(String(arch.name));
        } while (_findnext(hFile, &arch) == 0);
          _findclose(hFile);
    };

me muestra solo los archivos pero no las carpetas, si puedes darme una manito te lo volveria a agradecer.

Saludos. ^\||/

ecfisa 23-11-2013 20:05:49

Cita:

Empezado por darkmir (Mensaje 469987)
Hola ecfisa,

estoy amoldando el codigo, pero me lanza un error en una parte, este es el codigo modificado:

Código:

  char *Dir = "F:\\" ;
  struct _finddata_t arch;
  long hFile;
  char tmp[MAX_PATH];

  chdir(Dir); //cambio al directorio actual
  arch.attrib = _A_SUBDIR;  //define si el atributo es un directorio
  if( (hFile = _findfirst("*.*", &arch) ) != -1)
    {
      do{
          if (arch.attrib == _A_SUBDIR)
            {
              if (strcmp(arch.name,".")!= 0 && strcmp(arch.name,"..") != 0)
              {
              strcpy(tmp, Dir);
              strcat(tmp, "\\");
              strcat(tmp, arch.name);
              }
            }
          else
            ListBox1->Items->Add(String(arch.name));
        } while (_findnext(hFile, &arch) == 0);
          _findclose(hFile);
    };

me muestra solo los archivos pero no las carpetas, si puedes darme una manito te lo volveria a agradecer.

Saludos. ^\||/

Hola darkmir.

En ese caso estas omitiendo la llamada recursiva por lo que el código va a tener una funcionalidad similar al de tu primer mensaje ( #1 ). Es decir, no va a recorrer el árbol de carpetas y subcarpetas listándote los archivos.

Pero si incluís la línea que llama recursivamente a la función y la profundidad de la búsqueda es enorme provoca el desbordamiento de la pila, por este camino estas en un callejón sin salida. Lo que tenes que hacer es reformular el algoritmo para que funcione de forma iterativa.

En este enlace tenes ejemplos búsqueda sobre un árbol binario de ámbos modos: Árbol binario de búsqueda

Saludos :)

darkmir 28-11-2013 03:24:47

Gracias.
 
Hola ecfisa,

muy interesante lo de arboles y los ejemplo que estan en el link me estan sirviendo de ayuda, no sabia que existia ese metodo.

gracias.

darkmir 05-12-2013 19:53:16

Error encontrado
 
Hola ecfisa,

Encontre el error del cual no funcionaba la recursividad para lisar archivos, no es por la profundidad (cantidad de carpetas/subcarpetas hasta el nodo) que sea muy grande, ni que se agote la memoria destinada a la pila, es solo con los nombes de las carpetas.

La recursividad esta bien, me lista todos los archivos, lo probe con discos duros de 1 TB , el cual estaba lleno con 870 GB, y me los listo normal, pero si encuentra una carpeta con un nombre extenso , se cuelga y es en donde ya no funciona el programa.

Un ejemplo de nombre de carpeta el cual no lee es

"F:\\Theme Patcher if not ever made ​​the patches you here" ,

al llegar a leer una carpeta asi, se forma un bucle infinito y es ahí donde se agota toda la memoria, pero en base al nombre de la carpeta, sino funcionaria normal, dejo la imagen del error:


Pude arreglarlo con un try{}catch(...){} por el momento, si puedo omitir ese error por otro método lo posteare para mejorar el código.

Saludos.

escafandra 06-12-2013 20:56:53

Yo no le veo nada raro al código para que de un error con nombres largos. La Constante MAX_PATH debe valer 260. Los nombres más largos admitidos por NTFS es de 256 incluido todo el PATH, quizás el error viene por este lado.

Yo suelo usar sin problemas funciones recursivas para listar archivos como la que publiqué aquí hace unos años.

Saludos.

darkmir 06-12-2013 22:36:07

Gracias por el apoyo
 
Cita:

Empezado por escafandra (Mensaje 470491)
Yo no le veo nada raro al código para que de un error con nombres largos. La Constante MAX_PATH debe valer 260. Los nombres más largos admitidos por NTFS es de 256 incluido todo el PATH, quizás el error viene por este lado.

Yo suelo usar sin problemas funciones recursivas para listar archivos como la que publiqué aquí hace unos años.

Saludos.

gracias escafandra,

el código que me mandaste, me funciono perfectamente, ya no tuve problemas de cuelgue, bueno creo que este tema con las soluciones brindadas, ya esta solucionado.

Saludos y gracias.

ecfisa 07-12-2013 12:32:10

Hola.

Me quedó la duda del por qué del error, y según parece lo causa la función ChDir que es la diferencia significativa entre el código de escafandra que funciona correctamente y la segunda opción que te sugerí en el mensaje #2 que da el error. Pero ignoro por qué causa lo provoca, me imagino que no está preparada para soportar los nuevos nombres...

Para sumar opciones, otro modo es:
Código:

void ListFiles(String folder, TStrings *TS)
{
  WIN32_FIND_DATA wfd;
  HANDLE hFind;
  DWORD ERR;

  hFind = FindFirstFile(String(folder + "\\*.*").c_str(), &wfd);
  if (hFind != INVALID_HANDLE_VALUE) {
    do {
      if (String(wfd.cFileName) != "." && String(wfd.cFileName) != "..") {
        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          ListFiles(String(folder+"\\"+wfd.cFileName), TS);
        else
          TS->Add(String(folder+"\\"+wfd.cFileName));
      }
    } while(FindNextFile(hFind, &wfd));
    FindClose(&wfd);
  }
}

Llamada:
Código:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ListFiles("C:\\Windows", ListBox1->Items);
}

Saludos :)

darkmir 08-12-2013 18:15:35

Cita:

Empezado por ecfisa (Mensaje 470499)
Hola.

Me quedó la duda del por qué del error, y según parece lo causa la función ChDir que es la diferencia significativa entre el código de escafandra que funciona correctamente y la segunda opción que te sugerí en el mensaje #2 que da el error. Pero ignoro por qué causa lo provoca, me imagino que no está preparada para soportar los nuevos nombres...

Para sumar opciones, otro modo es:
Código:

void ListFiles(String folder, TStrings *TS)
{
  WIN32_FIND_DATA wfd;
  HANDLE hFind;
  DWORD ERR;

  hFind = FindFirstFile(String(folder + "\\*.*").c_str(), &wfd);
  if (hFind != INVALID_HANDLE_VALUE) {
    do {
      if (String(wfd.cFileName) != "." && String(wfd.cFileName) != "..") {
        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          ListFiles(String(folder+"\\"+wfd.cFileName), TS);
        else
          TS->Add(String(folder+"\\"+wfd.cFileName));
      }
    } while(FindNextFile(hFind, &wfd));
    FindClose(&wfd);
  }
}

Llamada:
Código:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ListFiles("C:\\Windows", ListBox1->Items);
}

Saludos :)

Hola ecfisa,

probe el código pero me manda este error , al escanear cualquier unidad:



algo que ver con la memoria, quizás?

Saludos.

ecfisa 08-12-2013 19:09:34

Hola darkmir.

Probé el código varias veces anteriormente y nuevamente ahora, esta vez copiando y pegándolo desde el mensaje para descartar cualquier error de transcripción, sigue funcionando sin errores.
Inclusive, a fines de prueba, cree un archivo con el nombre que mencionaste te daba error: "Theme Patcher if not ever made ​​the patches you here" y también otros con los nombres más rebuscados que se me ocurrieron.

No sé que te puede estar sucediendo y sobre los datos que dispongo no logro reproducir el error... Lo único que se me ocurre es que copies y pegues exáctamente el código que estas usando para poder revisarlo.

Saludos :)

darkmir 08-12-2013 19:23:06

Error
 
Hola ecfisa,

el código que estoy utilizando es este:

Código:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void ListFiles(String folder, TStrings *TS)
{
  WIN32_FIND_DATA wfd;
  HANDLE hFind;
  DWORD ERR;

  hFind = FindFirstFile(String(folder + "\\*.*").c_str(), &wfd);
  if (hFind != INVALID_HANDLE_VALUE) {
    do {
      if (String(wfd.cFileName) != "." && String(wfd.cFileName) != "..") {
        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          ListFiles(String(folder+"\\"+wfd.cFileName), TS);
        else
          TS->Add(String(folder+"\\"+wfd.cFileName));
      }
    } while(FindNextFile(hFind, &wfd));
    FindClose(&wfd);
  }
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ListFiles("E:\\", ListBox1->Items);
}

porseacaso te mando estos datos:
- version trial del Embarcadero c++ XE5.
- windows 7 64 bits.

quizás sea la version en la que se compila y ejecuta.

Saludos.

aguml 08-12-2013 23:48:31

a mi me ha pasado algo similar por ejemplo al aparecer caracteres especiales en el nombrel yo lo solucione combirtiendo el nombre en su nombre corto y luego uso su nombre corto para trabajar aunque sigas mostrando el largo. Tambien me han pasado errores similares cuando el nombre empezaba con un espacio. Si te fijas en el nombre del directorio te aparecen dos signos de interrogacion lo cual me da que pensar que en el nombre hay caracteres que no se pueden interpretar. Yo probaria a modificar el nombre del directorio escribiendolo a mano y poniendole el mismo pero repito, a mano ya que un copy paste lo dejaria igual.

ecfisa 09-12-2013 02:13:10

Hola darkmir.

Probé el código que publicaste y funciona correctamente.
Seguramente como comentas en tu último mensaje, se trate de alguna diferencia en las versiones, yo estoy usando C++ Builder 6.

De todos modos la idea era sólo poner una opción mas, ya tenes la solución con el código que te sugirió el amigo escafandra.

Saludos :)

darkmir 09-12-2013 20:51:07

Gracias
 
Hola ecfisa,

Gracias por el código, acabo de instalar builder c++ 6.0,lo probe y funciona correctamente, estuve viendo en otras webs, que hay algunas incompatibilidades de algunos codigos cuando se quiere migrar entre builder c++ 6 y el embarcadero XE 2010 para arriba, aún así ya se tiene una solución de este tema para ambos casos, y los que visiten este tema pueden encontrar su solución para la versión que tengan o deseen.

Saludos.^\||/

ecfisa 10-12-2013 16:43:15

Hola darkmir.

Sería estupendo si pudieras publicar los enlaces que hallaste en tu búsqueda para que otros compañeros los puedan aprovechar.

Por mi parte pude encontrar este: Unicode Migration Resources for Delphi, C++Builder and RAD Studio

Saludos :)


La franja horaria es GMT +2. Ahora son las 06:52:44.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi