Listado recursivo de un directorio en C# y VB

Listar de forma recursiva todos los archivos de un directorio y de los subdirectorios que contiene, informando del tiempo requerido para la tarea, con botones para ordenar la lista de forma ascendente o descendente, en Visual Studio 2017 con C# y VB

Damián Castroviejo tiene un ejercicio titulado «How to use a recursive function to read a directory structure and save it to XML» cuyo código consigue listar adecuadamente de forma recursiva los archivos de un directorio y de sus subdirectorios y guardar la estructura de archivos y carpetas en un archivo XML. Aplicando la función recursiva propuesta por Damián Castroviejo, en este ejercicio se rellena un control ListBox con todos los archivos existentes en una carpeta determinada.

El ejercicio tiene estas características:

  • seleccionar una carpeta y rellenar un control Listbox con todos los archivos que contiene, incluyendo los contenidos en las subcarpetas
  • informar al usuario del tiempo que se ha tardado en rellenar el control ListBox utilizando la propiedad Environment.TickCount, lo que equivale a una medición sencilla del rendimiento de esa tarea
  • ordenar la lista de archivos de forma ascendente, descendente o buscando una extensión concreta.

Inicio del programa

Para que el formulario arranque centrado en la pantalla en horizontal pero ocupando el área visible en vertical excepto un pequeño espacio de 140px se emplea este código:

// Al cargar el formulario: tamaño y posición (pos. horizontal = en el medio) (alto = alto de la pantalla - 140 px)
private void Form1_Load(System.Object sender, System.EventArgs e)
{
	this.Left = System.Convert.ToInt32((Screen.PrimaryScreen.WorkingArea.Width - this.Width) / 2);
	this.Top = 70;
	this.Height = (Screen.PrimaryScreen.WorkingArea.Height) - 140;
	this.Width = 535;
}

Mediante un cuadro de diálogo de selección de carpeta (FolderBrowserDialog) se elige el directorio cuyos archivos (y los de las subcarpetas) se van a listar.

// Ruta al directorio cuyos archivos van a ser listados
private string strPath;
// Matriz con las carpetas del directorio seleccionado
private string[] strCarpeta;
// Cuadro de diálogo de selección de carpeta (FolderBrowserDialog)
if (folderDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
	// strPath contiene la ruta al directorio elegido y strCarpeta una matriz con sus archivos
	strPath = folderDlg.SelectedPath;
}
strCarpeta = System.IO.Directory.GetFiles(strPath);
//
this.Text = "Dir2Files C#";

Rellenar el ListBox

Rellenar el control ListBox llamando a la función recursiva ListarArchivos1 a la que se le pasan 2 parámetros string, el nombre del directorio actual que cambia en cada pasada recursiva y la ruta completa al directorio actual. El ListBox se rellena sólo con nombres de archivo, sin carpetas. Al empezar la serie, ambas rutas coinciden (nombre y ruta del directorio actual).

Un bucle foreach recorre cada carpeta del directorio para obtener sus archivos. La función ListarArchivos1 es llamada de nuevo dentro de su propio código (por ello es recursiva) para obtener los archivos de las subcarpetas que puede haber dentro de cada carpeta. Otro bucle foreach obtiene la lista de archivos, en este caso la función no necesita ser recursiva ya que los archivos no contienen otros archivos dentro de ellos mismos.

Por otro lado se emplea Environment.TickCount que devuelve el nº de milisegundos desde que se ha iniciado Windows. Basado en ello, calculando la propiedad TickCount al comienzo y al final del procedimiento vemos su duración. Es una forma poco elaborada de comparar el rendimiento de procedimientos diferentes.

// Variables para contener información sobre archivos y carpetas
DirectoryInfo d = default(DirectoryInfo);
DirectoryInfo ofs = new DirectoryInfo(strPath + "\\");
FileInfo oFile = default(FileInfo);
//
// Bucle foreach para las carpetas: obtener todas las carpetas contenidas en el directorio seleccionado
foreach (DirectoryInfo tempLoopVar_d in ofs.GetDirectories())
{
	d = tempLoopVar_d;
	// Llamada recursiva (ListarArchivos1 es llamada desde dentro de ListarArchivos1)
	// Porque cada carpeta puede tener a su vez subcarpetas que también han de ser analizadas
	ListarArchivos1(d.Name, strPath + "\\" + d.Name);
}
//
// Bucle foreach para los archivos: obtener todos los archivos existentes en cada una de las carpetas y añadirlos al ListBox
// Este bucle no necesita llamadas recursivas porque un archivo no tiene subarchivos que recorrer
foreach (FileInfo tempLoopVar_oFile in ofs.GetFiles())
{
	oFile = tempLoopVar_oFile;
	// Distintas formas de mostrar la lista de archivos en el control ListBox
	//listBox.Items.Add((listBox.Items.Count + 1) + ". " + oFile.Name)
	//listBox.Items.Add(oFile.Name + " [" + (listBox.Items.Count + 1) + "]")
	listBox.Items.Add(oFile.Name);
}
// Environment.TickCount devuelve el nº de milisegundos desde que se ha iniciado Windows
// Calculando la propiedad TickCount al comienzo y al final del procedimiento vemos su duración
// Es una forma poco elaborada de comparar el rendimiento de procedimientos diferentes
inicio = System.Environment.TickCount;
//
// Rellenar el control ListBox llamando a la funcion recursiva ListarArchivos1
// a la que se le pasan 2 parámetros String:
//- nombre del directorio actual, cambia en cada pasada recursiva
//- ruta completa al directorio actual
// Al empezar la serie, ambas rutas coinciden (nombre y ruta del directorio actual)
listBox.Items.Clear();
ListarArchivos1(strPath + "\\", strPath + "\\");
//
j = listBox.Items.Count;
this.Text = "Archivos: " + Convert.ToString(j);
//
final = System.Environment.TickCount;
// Información del tiempo empleado en rellenar el control ListBox
MessageBox.Show("Tiempo utilizado en el proceso: " + Convert.ToString((double) (final - inicio) / 1000) + " segundos.");

Ordenar la lista de forma ascendente o descendente

El proceso de ordenar un ListBox es complejo por lo que se recurre a este «truco»:

  1. utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox
  2. volcamos el contenido del ListBox al ArrayList
  3. vaciamos el ListBox
  4. ordenamos el ArrayList
  5. lo volcamos al ListBox con los elementos ordenados.

Volvemos a emplear Environment.TickCount para saber el tiempo que ha tardado la tarea.

ArrayList matriz = new ArrayList();
int indice = 0;
//
inicio = System.Environment.TickCount;
//
this.Text = "Procesando el directorio elegido...";
//
for (indice = 0; indice <= listBox.Items.Count - 1; indice++)
{
	//matriz.Add(CStr(listBox.Items(indice)))
	matriz.Add(listBox.Items[indice]);
}
matriz.Sort();
listBox.Items.Clear();
//
if (b == true)
{
	// ordenar de manera ascendente
	for (indice = matriz.Count - 1; indice >= 0; indice--)
	{
		listBox.Items.Add(matriz[indice]);
	}
}
else
{
	// ordenar de manera descendente
	for (indice = 0; indice <= matriz.Count - 1; indice++)
	{
		listBox.Items.Add(matriz[indice]);
	}
}

Seleccionar los archivos por extensión

Una vez relleno el ListBox se puede elegir una extensión para que se muestren sólo los archivos que tengan esa extensión en su nombre. Primero se rellena el ListBox con la lista completa. Después utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox:

  1. pasamos al ArrayList los elementos del ListBox cuyo nombre termina en la extensión buscada
  2. vaciamos el ListBox
  3. volcamos el ArrayList al ListBox con los elementos encontrados.
// Si el cuadro de texto txtExt1 no está vacío
if (txtExt1.Text.Length > 0)
{
	//
	inicio = System.Environment.TickCount;
	//
	// rellenar el ListBox (para buscar siempre en la lista completa y no
	// en la de la extensión que se haya usado previamente)
	listBox.Items.Clear();
	ListarArchivos1(strPath + "\\", strPath + "\\");
	j = listBox.Items.Count;
	//
	// Utilizamos un ArrayList como almacén temporal de la lista de archivos del ListBox
	// - pasamos al ArrayList los elementos del ListBox cuyo nombre termina en la extensión buscada
	// - vaciamos el ListBox
	// - volcamos el ArrayList al ListBox con los elementos encontrados
	ArrayList matriz = new ArrayList();
	int indice = 0;
	//
	for (indice = 0; indice <= listBox.Items.Count - 1; indice++)
	{
		if (Convert.ToString(listBox.Items[indice]).EndsWith("." + txtExt1.Text, StringComparison.CurrentCultureIgnoreCase))
		{
			matriz.Add(Convert.ToString(listBox.Items[indice]));
		}
	}
	listBox.Items.Clear();
	//
	for (indice = 0; indice <= matriz.Count - 1; indice++)
	{
		listBox.Items.Add(matriz[indice]);
	}
}

Información en la barra del formulario

En todos los pasos del programa mostramos en la barra de título información sobre el nº de archivos mostrados:

// Información en la barra de título
j = listBox.Items.Count;
this.Text = "Archivos ." + txtExt1.Text + ": " + Convert.ToString(j);

Puedes descargar un archivo ZIP con las aplicaciones C# y VB.

Nota: el ajuste automático de escala del formulario que muestra la ventana de la aplicación se puede configurar de varias maneras, lo tienes en las propiedades del formulario como AutoScaleMode, la opción con la que obtengo el mejor resultado es con Dpi.

2 comentarios en «Listado recursivo de un directorio en C# y VB»

  1. He usado & durante muchos años ya que era la tradición de Visual Basic pero al poner fragmentos de código en el blog he observado que WordPress formatea algunos caracteres (como hace con & por ejemplo) y da menos problemas mostrar el carácter + que & en esos fragmentos de código. Ese sería el principal motivo.

    Pero muchos programadores expertos recomiendan usar + por similitud con tantos lenguajes que así lo hacen, como C# y Java, por ejemplo.

    NOTA: si se usa + para concatenar cadenas hay que tener en cuenta que si se une una cadena y un número (por ej: «cadena» + 4) hay que hacer conversión del número para que no de error de compilación (ej: «cadena» + Cstr(4)).

  2. Veo que usas el signo + en vez de & para concatenar cadenas, sé que ambos son válidos pero ¿por qué motivo prefieres +?

Los comentarios están cerrados.