2009-06-11 29 views
12

Quiero escanear un árbol de directorios y enumerar todos los archivos y carpetas dentro de cada directorio. Creé un programa que descarga imágenes desde una cámara web y las guarda localmente. Este programa crea un archivo de archivo basado en el momento en que se descarga la imagen. Ahora quiero escanear estas carpetas y subir las imágenes a un servidor web, pero no estoy seguro de cómo puedo escanear los directorios para encontrar las imágenes. Si alguien pudiera publicar algún código de muestra, sería muy útil.exploración recursiva de carpetas en C++

edición: Estoy funcionando esto en un sistema Linux embebido y no quieres usar impulso

+0

¿Qué sistema operativo? –

Respuesta

30

Consulte man ftw para obtener un sencillo "árbol de archivos a pie". También usé fnmatch en este ejemplo.

#include <ftw.h> 
#include <fnmatch.h> 

static const char *filters[] = { 
    "*.jpg", "*.jpeg", "*.gif", "*.png" 
}; 

static int callback(const char *fpath, const struct stat *sb, int typeflag) { 
    /* if it's a file */ 
    if (typeflag == FTW_F) { 
     int i; 
     /* for each filter, */ 
     for (i = 0; i < sizeof(filters)/sizeof(filters[0]); i++) { 
      /* if the filename matches the filter, */ 
      if (fnmatch(filters[i], fpath, FNM_CASEFOLD) == 0) { 
       /* do something */ 
       printf("found image: %s\n", fpath); 
       break; 
      } 
     } 
    } 

    /* tell ftw to continue */ 
    return 0; 
} 

int main() { 
    ftw(".", callback, 16); 
} 

(Ni siquiera compilación probado, pero se entiende la idea.)

Esto es mucho más simple que tratar con DIRENT s y recorrido recursivo a sí mismo.


Para un mayor control sobre el recorrido, también hay fts. En este ejemplo, se omiten los archivos de puntos (archivos y directorios con nombres que comienzan con "."), A menos que se pasen explícitamente al programa como punto de partida.

#include <fts.h> 
#include <string.h> 

int main(int argc, char **argv) { 
    char *dot[] = {".", 0}; 
    char **paths = argc > 1 ? argv + 1 : dot; 

    FTS *tree = fts_open(paths, FTS_NOCHDIR, 0); 
    if (!tree) { 
     perror("fts_open"); 
     return 1; 
    } 

    FTSENT *node; 
    while ((node = fts_read(tree))) { 
     if (node->fts_level > 0 && node->fts_name[0] == '.') 
      fts_set(tree, node, FTS_SKIP); 
     else if (node->fts_info & FTS_F) { 
      printf("got file named %s at depth %d, " 
       "accessible via %s from the current directory " 
       "or via %s from the original starting directory\n", 
       node->fts_name, node->fts_level, 
       node->fts_accpath, node->fts_path); 
      /* if fts_open is not given FTS_NOCHDIR, 
      * fts may change the program's current working directory */ 
     } 
    } 
    if (errno) { 
     perror("fts_read"); 
     return 1; 
    } 

    if (fts_close(tree)) { 
     perror("fts_close"); 
     return 1; 
    } 

    return 0; 
} 

Una vez más, no es ni probado en compilación ni probado, pero pensé que lo mencionaría.

+0

La muestra FTS funcionó de maravilla. Los únicos cambios que tuve que hacer fueron "ftsread" -> "fts_read" y tuve que convertir el resultado de fts_read en (FTSENT *). Hubiera pensado que encontrar un código como este habría sido más fácil en la web, pero este fue definitivamente el ejemplo más limpio que encontré. Gracias! – Hortitude

+0

Gracias por los ejemplos, eso es útil –

+1

Los valores para fts_info no son bits individuales ... las opciones FTS_D a FTS_W se definen como valores secuenciales 1 a 14. Probablemente sea confuso para algunos tener el código como "& FTS_F" sin comentario explicando qué pasa. Es POSIBLE gente destinada a tomar FTS_F, FTS_INIT, FTS_NS, FTS_NSOK, FTS_SL, FTS_SLNONE y FTS_W aquí ... pero FTS_NS o FTS_NSOK sería un poco extraño. Además, veo el código en la web haciendo "& FTS_D", que casi con seguridad no es lo que pretendían (obtiene FTS_ERR, FTS_DEFAULT). – darron

6

Boost.Filesystem le permite hacer eso. ¡Mira el docs!

EDIT:
Si está utilizando Linux y no desea utilizar Boost, tendrá que utilizar las funciones C nativas de Linux. This page muestra muchos ejemplos sobre cómo hacer precisamente eso.

+1

el último enlace está roto –

0

Creo que si puede usar Qt/Embedded, existen clases QDir y QFileInfo que pueden ayudarlo, aunque depende de si puede usar el Qt. La pregunta es qué API proporciona su sistema.

1

Querrá utilizar las funciones de directorio declaradas en dirent.h. Este wikipedia page los describe e incluye código de muestra. Para su aplicación, una vez que haya identificado un directorio, querrá llamar nuevamente a la función de procesamiento recursivamente para procesar el contenido del directorio.

1

También puede usar glob/globfree.

+0

Es más fácil que leer directorios y realizar coincidencias usted mismo, pero aún no es recursivo. De acuerdo, usar GLOB_ONLYDIR significa que podrías evitar lidiar con DIRENT por completo, pero aún no es tan útil como ftw, y los cruces basados ​​en el nombre son rabiosos. – ephemient

2

¡Soy vieja escuela, no ftw() para mí! Esto es crudo (ha pasado un tiempo desde que hice la programación directa en C), y muchas cosas están codificadas, y probablemente arruiné mis cálculos de longitud para las funciones strnc *(), pero ya entendiste la idea. Hay un ejemplo similar en K & R por cierto.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include <sys/types.h> 
#include <dirent.h> 

void listdir(char* dirname, int lvl); 

int main(int argc, char** argv) 
{ 

    if (argc != 2) { 
    fprintf(stderr, "Incorrect usage!\n"); 
    exit(-1); 
    } 
    listdir(argv[1], 0); 


    return 0; 
} 

void listdir(char* dirname, int lvl) 
{ 

    int i; 
    DIR* d_fh; 
    struct dirent* entry; 
    char longest_name[4096]; 

    while((d_fh = opendir(dirname)) == NULL) { 
    fprintf(stderr, "Couldn't open directory: %s\n", dirname); 
    exit(-1); 
    } 

    while((entry=readdir(d_fh)) != NULL) { 

    /* Don't descend up the tree or include the current directory */ 
    if(strncmp(entry->d_name, "..", 2) != 0 && 
     strncmp(entry->d_name, ".", 1) != 0) { 

     /* If it's a directory print it's name and recurse into it */ 
     if (entry->d_type == DT_DIR) { 
     for(i=0; i < 2*lvl; i++) { 
      printf(" "); 
     } 
     printf("%s (d)\n", entry->d_name); 

     /* Prepend the current directory and recurse */ 
     strncpy(longest_name, dirname, 4095); 
     strncat(longest_name, "/", 4095); 
     strncat(longest_name, entry->d_name, 4095); 
     listdir(longest_name, lvl+1); 
     } 
     else { 

     /* Print some leading space depending on the directory level */ 
     for(i=0; i < 2*lvl; i++) { 
      printf(" "); 
     } 
     printf("%s\n", entry->d_name); 
     } 
    } 
    } 

    closedir(d_fh); 

    return; 
} 
+0

strncmp (nombre, ".", 1) excluirá los archivos de puntos. Además, probablemente sea mejor omitir (con advertencia) que intentar proceder con un nombre truncado. Por último, en lugar de un bucle, hay un bonito truco para imprimir sangrías: espacios de caracteres estáticos [] = ""; printf ("% s", espacios [max (0, strlen (espacios) - count)]); – ephemient

Cuestiones relacionadas