2010-02-13 25 views
34

¿Cómo eliminar un directorio no vacío en C o C++? ¿Hay alguna función? rmdir solo borra el directorio vacío. Proporcione una forma sin usar ninguna biblioteca externa.Eliminar un directorio no vacío mediante programación en C o C++

¿También dime cómo eliminar un archivo en C o C++?

+0

gente downvote una pregunta que no encuentran clara o útil. Dado que tu pregunta parece lo suficientemente clara, creo que alguien (no yo) no la encontró útil. – Manuel

+5

No existe un lenguaje como C/C++ –

+1

Tal vez fue downvoted porque la pregunta se ha realizado aquí muchas veces, por ejemplo http://stackoverflow.com/questions/1149764/delete-folder-with-items, y personas están hartos de verlo? El que menospreciaba no era yo, por cierto. –

Respuesta

21

Desea escribir una función (una función recursiva es más fácil, pero puede fácilmente quedarse sin espacio de pila en directorios profundos) que enumerará los elementos secundarios de un directorio. Si encuentra un niño que es un directorio, recurse en eso. De lo contrario, eliminas los archivos dentro. Cuando haya terminado, el directorio está vacío y puede eliminarlo a través de syscall. Para enumerar directorios en Unix, puede usar opendir, readdir y closedir. Para eliminarlo, utilice rmdir() en un directorio vacío (es decir, al final de su función, después de eliminar los elementos secundarios) y unlink() en un archivo. Tenga en cuenta que en muchos sistemas no se admite el miembro d_type en struct dirent; en estas plataformas, deberá usar stat() y S_ISDIR(stat.st_mode) para determinar si una ruta dada es un directorio.

En Windows, que va a utilizar FindFirstFile()/FindNextFile() enumerar, RemoveDirectory() en directorios vacíos, y DeleteFile() para eliminar archivos.

He aquí un ejemplo que podría funcionar en Unix (no está comprobado):

int remove_directory(const char *path) 
{ 
    DIR *d = opendir(path); 
    size_t path_len = strlen(path); 
    int r = -1; 

    if (d) 
    { 
     struct dirent *p; 

     r = 0; 

     while (!r && (p=readdir(d))) 
     { 
      int r2 = -1; 
      char *buf; 
      size_t len; 

      /* Skip the names "." and ".." as we don't want to recurse on them. */ 
      if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) 
      { 
      continue; 
      } 

      len = path_len + strlen(p->d_name) + 2; 
      buf = malloc(len); 

      if (buf) 
      { 
      struct stat statbuf; 

      snprintf(buf, len, "%s/%s", path, p->d_name); 

      if (!stat(buf, &statbuf)) 
      { 
       if (S_ISDIR(statbuf.st_mode)) 
       { 
        r2 = remove_directory(buf); 
       } 
       else 
       { 
        r2 = unlink(buf); 
       } 
      } 

      free(buf); 
      } 

      r = r2; 
     } 

     closedir(d); 
    } 

    if (!r) 
    { 
     r = rmdir(path); 
    } 

    return r; 
} 
+0

respuesta razonable, pero "agotar fácilmente de stackspace"? – peterchen

+0

@peterchen Sí. Lo he visto suceder También es más fácil en Windows que en POSIX, porque 'WIN32_FIND_DATA' es enorme, mientras que' DIR * 'y' struct dirent * 'son solo dos indicadores ... – asveikau

+4

La primera vez que hice esto, no revisé para" .. " . Y sí, el resultado es que el programa cambia el directorio hasta c :, ¡y luego comienza a eliminar desde allí! "Afortunadamente", sucedió en el trabajo. :) –

1

Puede utilizar opendir y readdir para leer las entradas de directorio y desvincular para eliminarlos.

0

unlink eliminará un archivo.

remove también eliminará un archivo pero es más portátil.

Puede probar system("rm -r ./path") si está trabajando en Linux, de lo contrario también hay una función de eliminación recursiva de la API de Windows.

+30

'system()' es casi siempre la respuesta incorrecta. – asveikau

+5

Tiene sus usos, es una solución rápida y sucia. – Xorlev

+0

¿cuál es la función de eliminación recursiva de la API de Windows? – Manuel

13

La forma más fácil de hacerlo es con remove_all función de la biblioteca Boost.Filesystem. Además, el código resultante será portátil.

Si desea escribir algo específico para Unix (rmdir) o para Windows (RemoveDirectory), deberá escribir una función que elimine de forma recursiva subarchivos y subcarpetas.

EDITAR

Parece que esta pregunta fue already asked, de hecho alguien ya recomienda REMOVE_ALL de Boost. Así que por favor no voten mi respuesta.

+2

brb descargando la biblioteca Boost.Filesystem en mi virtualbox DOS para usar en mi compilador turbo c. – Dmitry

13

Muchos sistemas de tipo unix (Linux, los BSD y OS X, como mínimo) tienen las funciones fts para el recorrido del directorio. Para eliminar un directorio recursivamente, solo realice un recorrido transversal en profundidad (sin seguir enlaces simbólicos) y elimine todos los archivos visitados.

int recursive_delete(const char *dir) 
{ 
    int ret = 0; 
    FTS *ftsp = NULL; 
    FTSENT *curr; 

    // Cast needed (in C) because fts_open() takes a "char * const *", instead 
    // of a "const char * const *", which is only allowed in C++. fts_open() 
    // does not modify the argument. 
    char *files[] = { (char *) dir, NULL }; 

    // FTS_NOCHDIR - Avoid changing cwd, which could cause unexpected behavior 
    //    in multithreaded programs 
    // FTS_PHYSICAL - Don't follow symlinks. Prevents deletion of files outside 
    //    of the specified directory 
    // FTS_XDEV  - Don't cross filesystem boundaries 
    ftsp = fts_open(files, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL); 
    if (!ftsp) { 
     fprintf(stderr, "%s: fts_open failed: %s\n", dir, strerror(errno)); 
     ret = -1; 
     goto finish; 
    } 

    while ((curr = fts_read(ftsp))) { 
     switch (curr->fts_info) { 
     case FTS_NS: 
     case FTS_DNR: 
     case FTS_ERR: 
      fprintf(stderr, "%s: fts_read error: %s\n", 
        curr->fts_accpath, strerror(curr->fts_errno)); 
      break; 

     case FTS_DC: 
     case FTS_DOT: 
     case FTS_NSOK: 
      // Not reached unless FTS_LOGICAL, FTS_SEEDOT, or FTS_NOSTAT were 
      // passed to fts_open() 
      break; 

     case FTS_D: 
      // Do nothing. Need depth-first search, so directories are deleted 
      // in FTS_DP 
      break; 

     case FTS_DP: 
     case FTS_F: 
     case FTS_SL: 
     case FTS_SLNONE: 
     case FTS_DEFAULT: 
      if (remove(curr->fts_accpath) < 0) { 
       fprintf(stderr, "%s: Failed to remove: %s\n", 
         curr->fts_path, strerror(errno)); 
       ret = -1; 
      } 
      break; 
     } 
    } 

finish: 
    if (ftsp) { 
     fts_close(ftsp); 
    } 

    return ret; 
} 
+1

Muy buen ejemplo de código y explicación: esta debería ser la respuesta aceptada. –

+0

La interfaz 'fts' [no está disponible en musl libc] (http://wiki.musl-libc.org/wiki/FAQ#Q:_why_is_fts.h_not_included_.3F). –

0
//====================================================== 
// Recursely Delete files using: 
// Gnome-Glib & C++11 
//====================================================== 

#include <iostream> 
#include <string> 
#include <glib.h> 
#include <glib/gstdio.h> 

using namespace std; 

int DirDelete(const string& path) 
{ 
    const gchar* p; 
    GError* gerr; 
    GDir*  d; 
    int  r; 
    string ps; 
    string path_i; 
    cout << "open:" << path << "\n"; 
    d  = g_dir_open(path.c_str(), 0, &gerr); 
    r  = -1; 

    if (d) { 
     r = 0; 

     while (!r && (p=g_dir_read_name(d))) { 
      ps = string{p}; 
      if (ps == "." || ps == "..") { 
      continue; 
      } 

      path_i = path + string{"/"} + p; 


      if (g_file_test(path_i.c_str(), G_FILE_TEST_IS_DIR) != 0) { 
      cout << "recurse:" << path_i << "\n"; 
      r = DirDelete(path_i); 
      } 
      else { 
      cout << "unlink:" << path_i << "\n"; 
      r = g_unlink(path_i.c_str()); 
      } 
     } 

     g_dir_close(d); 
    } 

    if (r == 0) { 
     r = g_rmdir(path.c_str()); 
    cout << "rmdir:" << path << "\n"; 

    } 

    return r; 
} 
+0

Rodear tu código con una explicación mejoraría seriamente tu respuesta. – zx485

7

Si está utilizando un sistema operativo compatible con POSIX, se puede usar para nftw() recorrido del árbol de archivos y eliminar (elimina archivos o directorios). Si estás en C++ y tu proyecto usa boost, no es una mala idea usar Boost.Filesystem como lo sugiere Manuel.

En el ejemplo de código siguiente decidí no pasen por enlaces simbólicos y puntos de montaje (sólo para evitar una gran extracción :)):

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

static int rmFiles(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) 
{ 
    if(remove(pathname) < 0) 
    { 
     perror("ERROR: remove"); 
     return -1; 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    if (argc != 2) 
    { 
     fprintf(stderr,"usage: %s path\n",argv[0]); 
     exit(1); 
    } 

    // Delete the directory and its contents by traversing the tree in reverse order, without crossing mount boundaries and symbolic links 

    if (nftw(argv[1], rmFiles,10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) 
    { 
     perror("ERROR: ntfw"); 
     exit(1); 
    } 

    return 0; 
} 
+0

Me alegra que alguien haya mencionado sobre 'ntfw'. :) –

Cuestiones relacionadas