Nuestro software debe buscar archivos dentro de un directorio determinado, ya sea un archivo especial, una recopilación de datos del disco duro, una búsqueda de plugins, etc…
Hemos elegido lenguaje C para hacer esto, porque en bash podemos llamar a ls o find y apaga y vámonos.
Estas funciones, nos recordarán al uso de archivos con fopen() y fclose().
Para usar esta implementación, como veremos en el ejemplo, debemos incluir <sys/types.c> y <dirent.h> y, en principio, empezaremos con un ejemplo sencillo que lista los archivos del directorio actual (una vez tenemos el nombre y la ruta del archivo podemos hacer con él lo que queramos):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <dirent.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Función que hace algo con un archivo */ void procesoArchivo(char *archivo); int main() { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; /* Empezaremos a leer en el directorio actual */ dir = opendir ("."); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */ /* Leyendo uno a uno todos los archivos que hay */ while ((ent = readdir (dir)) != NULL) { /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */ if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */ procesoArchivo(ent->d_name); } } closedir (dir); return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } void procesoArchivo(char *archivo) { /* Para "procesar", o al menos, hacer algo con el archivo, vamos a decir su tamaño en bytes */ /* para ello haremos lo que vemos aquí: https://poesiabinaria.net/2010/04/tamano-de-un-fichero-en-c/ */ FILE *fich; long ftam; fich=fopen(archivo, "r"); if (fich) { fseek(fich, 0L, SEEK_END); ftam=ftell(fich); fclose(fich); /* Si todo va bien, decimos el tamaño */ printf ("%30s (%ld bytes)\n", archivo, ftam); } else /* Si ha pasado algo, sólo decimos el nombre */ printf ("%30s (No info.)\n", archivo); } |
Este es un ejemplo sencillo, podemos profundizar un poco más. Vemos que readdir() nos devuelve todo lo que hay, y a veces, es muy útil diferenciar entre un fichero, un directorio, un enlace, un socket, un fifo, etc… para eso, en un principio utilizaremos la información del mismo dirent, que a veces la encontramos de forma rápida, aunque existen sistemas de archivos que no nos facilitan esa información de forma tan inmediata y tenemos que hacer stat(), un ejemplo de ello es nfs, ya que es significativamente más lento obtener esa información, si no la necesitamos, no intenta obtenerla, es para esos casos cuando obligamos a su obtención con stat().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <string.h> #include <errno.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Calculamos el tamaño del archivo */ long fileSize(char *fname); /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */ unsigned char statFileType(char *fname); /* Función que hace algo con un archivo, pero le pasamos el dirent completo, usaremos más datos */ void procesoArchivo(char *ruta, struct dirent *ent); int main(int argc, char *argv[]) { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; if (argc != 2) { error("Uso: ./directorio_2 <ruta>\n"); } /* Empezaremos a leer en el directorio actual */ dir = opendir (argv[1]); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); /* Una vez nos aseguramos de que no hay error, ¡vamos a jugar! */ /* Leyendo uno a uno todos los archivos que hay */ while ((ent = readdir (dir)) != NULL) { /* Nos devolverá el directorio actual (.) y el anterior (..), como hace ls */ if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { /* Una vez tenemos el archivo, lo pasamos a una función para procesarlo. */ procesoArchivo(argv[1], ent); } } closedir (dir); return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } long fileSize(char *fname) { FILE *fich; long ftam=-1; fich=fopen(fname, "r"); if (fich) { fseek(fich, 0L, SEEK_END); ftam=ftell(fich); fclose(fich); } else printf("ERRNO: %d - %s\n", errno, strerror(errno)); return ftam; } void procesoArchivo(char *ruta, struct dirent *ent) { long ftam; char *nombrecompleto; char strtam[20]; char strtipo[30]=""; /* Tiene que ser del mismo tipo de dirent.d_type en nuestro sistema */ static unsigned char tipoID[7]={DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK}; static char* tipoSTRs[7]={"Dispositivo de bloques", "Dispositivo de caracteres", "Directorio", "FIFO", "Enlace", "Archivo regular", "Socket Unix"}; int i; int tmp; unsigned char tipo; /* Sacamos el nombre completo con la ruta del archivo */ tmp=strlen(ruta); nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */ if (ruta[tmp-1]=='/') sprintf(nombrecompleto,"%s%s", ruta, ent->d_name); else sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name); /* Calcula el tamaño */ ftam=fileSize(nombrecompleto); if (ftam>=0) sprintf(strtam, "%ld bytes", ftam); else strcpy(strtam, "No info"); /* A veces ent->d_type no nos dice nada, eso depende del sistema de archivos que estemos */ /* mirando, por ejemplo ext*, brtfs, sí nos dan esta información. Por el contrario, nfs */ /* no nos la da (directamente, una vez que hacemos stat sí lo hace), y es en estos casos donde probamos con stat() */ tipo=ent->d_type; if (tipo==DT_UNKNOWN) tipo=statFileType(nombrecompleto); if (tipo!=DT_UNKNOWN) { /* Podíamos haber hecho un switch con los tipos y devolver la cadena, pero me da la impresión de que así es menos costoso de escribir. */ i=0; while ( (i<7) && (tipo!=tipoID[i]) ) ++i; if (i<7) strcpy(strtipo, tipoSTRs[i]); } /* Si no hemos encontrado el tipo, éste será desconocido */ if (strtipo[0]=='\0') strcpy(strtipo, "Tipo desconocido"); printf ("%30s (%s)\t%s \n", ent->d_name, strtam, strtipo); free(nombrecompleto); } /* stat() vale para mucho más, pero sólo queremos el tipo ahora */ unsigned char statFileType(char *fname) { struct stat sdata; /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */ if (stat(fname, &sdata)==-1) { return DT_UNKNOWN; } switch (sdata.st_mode & S_IFMT) { case S_IFBLK: return DT_BLK; case S_IFCHR: return DT_CHR; case S_IFDIR: return DT_DIR; case S_IFIFO: return DT_FIFO; case S_IFLNK: return DT_LNK; case S_IFREG: return DT_REG; case S_IFSOCK: return DT_SOCK; default: return DT_UNKNOWN; } } |
Un ejemplo más grande, contamos los archivos que hay en un árbol de directorios. Uno a uno, pero de forma recursiva (cuando encontramos un directorio nos meteremos dentro y llamaremos de nuevo a la función principal (cuentaArchivos()):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <string.h> #include <errno.h> /* Función para devolver un error en caso de que ocurra */ void error(const char *s); /* Calculamos el tamaño del archivo */ long fileSize(char *fname); /* Sacamos el tipo de archivo haciendo un stat(), es como el stat de la línea de comandos */ unsigned char statFileType(char *fname); /* Intenta sacar el tipo de archivo del ent */ unsigned char getFileType(char *ruta, struct dirent *ent); /* Obtiene el nombre del fichero con la ruta completa */ char *getFullName(char *ruta, struct dirent *ent); /* Genera una cadena de espacios, para dibujar el árbol */ char *generaPosStr(int niv); /* Función principal, que cuenta archivos */ unsigned cuentaArchivos(char *ruta, int niv); int main(int argc, char *argv[]) { unsigned num; if (argc != 2) { error("Uso: ./directorio_2 <ruta>\n"); } printf("Entrando en: %s\n", argv[1]); num=cuentaArchivos(argv[1], 1); printf("%s . Total: %u archivos\n", argv[1], num); /* Empezaremos a leer en el directorio actual */ return EXIT_SUCCESS; } void error(const char *s) { /* perror() devuelve la cadena S y el error (en cadena de caracteres) que tenga errno */ perror (s); exit(EXIT_FAILURE); } char *getFullName(char *ruta, struct dirent *ent) { char *nombrecompleto; int tmp; tmp=strlen(ruta); nombrecompleto=malloc(tmp+strlen(ent->d_name)+2); /* Sumamos 2, por el \0 y la barra de directorios (/) no sabemos si falta */ if (ruta[tmp-1]=='/') sprintf(nombrecompleto,"%s%s", ruta, ent->d_name); else sprintf(nombrecompleto,"%s/%s", ruta, ent->d_name); return nombrecompleto; } char *generaPosStr(int niv) { int i; char *tmp=malloc(niv*2+1); /* Dos espacios por nivel más terminador */ for (i=0; i<niv*2; ++i) tmp[i]=' '; tmp[niv*2]='\0'; return tmp; } unsigned cuentaArchivos(char *ruta, int niv) { /* Con un puntero a DIR abriremos el directorio */ DIR *dir; /* en *ent habrá información sobre el archivo que se está "sacando" a cada momento */ struct dirent *ent; unsigned numfiles=0; /* Ficheros en el directorio actual */ unsigned char tipo; /* Tipo: fichero /directorio/enlace/etc */ char *nombrecompleto; /* Nombre completo del fichero */ char *posstr; /* Cadena usada para posicionarnos horizontalmente */ dir = opendir (ruta); /* Miramos que no haya error */ if (dir == NULL) error("No puedo abrir el directorio"); while ((ent = readdir (dir)) != NULL) { if ( (strcmp(ent->d_name, ".")!=0) && (strcmp(ent->d_name, "..")!=0) ) { nombrecompleto=getFullName(ruta, ent); tipo=getFileType(nombrecompleto, ent); if (tipo==DT_REG) { ++numfiles; } else if (tipo==DT_DIR) { posstr=generaPosStr(niv); printf("%sEntrando en: %s\n", posstr, nombrecompleto); printf("%s%s . Total: %u archivos ", posstr, nombrecompleto, cuentaArchivos(nombrecompleto, niv+1)); /* Podemos poner las líneas que queramos */ printf("\n"); free(posstr); } free(nombrecompleto); } } closedir (dir); return numfiles; } unsigned char getFileType(char *nombre, struct dirent *ent) { unsigned char tipo; tipo=ent->d_type; if (tipo==DT_UNKNOWN) { tipo=statFileType(nombre); } return tipo; } /* stat() vale para mucho más, pero sólo queremos el tipo ahora */ unsigned char statFileType(char *fname) { struct stat sdata; /* Intentamos el stat() si no funciona, devolvemos tipo desconocido */ if (stat(fname, &sdata)==-1) { return DT_UNKNOWN; } switch (sdata.st_mode & S_IFMT) { case S_IFBLK: return DT_BLK; case S_IFCHR: return DT_CHR; case S_IFDIR: return DT_DIR; case S_IFIFO: return DT_FIFO; case S_IFLNK: return DT_LNK; case S_IFREG: return DT_REG; case S_IFSOCK: return DT_SOCK; default: return DT_UNKNOWN; } } |
Se puede descargar todo el código fuente desde aquí: Listado de archivos dentro de un directorio en C (3.5Kb)
Actualización (14/10/2011): En el blog código para llevar [el blog ya no está disponible] encontramos un ejemplo de cómo utilizar este código para encontrar el número de núcleos de nuestra CPU.
Pingback: Bitacoras.com /
un puntito en bitacoras, un abrazo
Muchas gracias !!
Pingback: Poesía binaria » Buscando un proceso en C /
Es muy util, muchas gracias
Pingback: Conocer en número de nucleos en Unix [C] /
@kenkeiras
Gracias a ti por comentar y por enlazarme! 🙂
Muy interesante y útil tambien
Gracias Aegywng!
Muy explicativo y con un nivel de detalle y comentarios muy alto. Felicidades, me ha sido de gran ayuda para repasar ciertos conceptos y para aprender algunos trucos.
Un saludo.
Muchas gracias por tu comentario DLM !
:O muchas gracias!! muy util!!
una pregunta… al obtener la ruta de los archivos en 1 directorio… tambien obtiene el directorio de las carpetas contenidas en esa carpeta??? osea, digamos q el directorio es asi:
dir\file1.txt
dir\file2.txt
dir\dir1\file3.txt
dir\dir2\file4.txt
al obtener los archivos dentro de la direccion «dir», tambien obtendra las rutas de los arhivos en las carpetas «dir1» y «dir2»??? o.O?
Muchas gracias por tu comentario. Si miras el último ejemplo, puedes ejecutar el algoritmo de forma recursiva para que una vez que encuentre un directorio, entre en él y siga explorando archivos.
Muy buenos ejemplos!!, de gran ayuda, Muchas gracias
Muchas gracias por tu comentario !
Pingback: Enlazado dinámico en C++ (dynamic linking) IV: Permitir plug-ins en nuestra aplicación | Poesía Binaria /
genial Exito!!
Gracias!
hola, estoy listando unos archivos que con otro programa en c si puedo abrir, pero con este ejemplo solamente imprimo el nombre y no el tamaño, es decir el puntero FILE *fich este retornando como nulo y no puedo abrir ningun archivo, que puede estar sucediendo?
he revisado los permisos y tienen todos los permisos, son archivos de texto
gracias!
problema resuelto: tenia el ejecutable del programa en una ruta y levantaba los archivos desde otra ruta, y no especificaba bien la misma
Perdona que no te contestara antes, estaba con el trabajo y luego vi que lo habías arreglado.
hola muy bueno, fijate q me marca error que dice que es invalida la conversion de malloc de void a int. :s podrias ayudarme plis
Me puedes pasar tu código? A lo mejor ha faltado el * para indicar el puntero en la respuesta de void.
Pingback: Conversaciones sobre Programación. Build 2014.01 | Linea de Codigo /
Perdon la pregunta, ya que puede resultar obvia… Llevo un buen tiempo sin programar en C y me doy cuenta de que olvide muchas cosas… Estoy intentando enviar a revisar un directorio en especifico, tengo la variable C y quiero agregar :\ para que pueda buscar la ruta… Sin embargo no me permite agregar estos valores
dir = opendir(ruta + «:\\»);
Cual seria la forma correcta de agregar esos dos caracteres mas?
Muchas gracias por el apoyo y es un aportaso… realmente me ha servido mucho
@Ivan
Hola Ivan,
Para hacerlo en C de toda la vida, tienes que concatenar ese :\\ a ruta, para ello, puedes utilizar strcat(ruta, «:\\»); y luego opendir(ruta); aunque estarás modificando la variable ruta. También puedes crearte otra variables para una cadena nueva y hacer: sprintf(minuevavariable, «%s:\\», ruta); y luego opendir(minuevavariable).
minuevavariable deberá ser un array de char o un puntero al que previamente le reserves espacio:
char minuevavariable[50]; // 50 por poner un valor…
o
char *minuevavariable = malloc(sizeof(char)* ( strlen(ruta)+ 3) ); // +3 porque añadiremos :\\ (dos caracteres, más el terminador \0)
Saludos
Pingback: Cómo listar archivos de forma recursiva en C, y un mundo de posibilidades en nuestros programas – Poesía Binaria /
Pingback: El 2016 para Poesía Binaria. Estadísticas, agradecimientos, redes y SQL. Y sólo es el principio – Poesía Binaria /
Perdona que no te contestara antes, estaba con el trabajo y luego vi que lo habías arreglado. Sump Pump Installation
So useful! Thanks for sharing this one. audio visual company