2010-02-13 7 views
34

Jak usunąć niepusty katalog w C lub C++? Czy jest jakaś funkcja? rmdir usuwa jedynie pusty katalog. Podaj sposób bez korzystania z zewnętrznej biblioteki.Programowanie programistyczne usuwania niepustego katalogu w C lub C++

Powiedz mi również, jak usunąć plik w C lub C++?

+0

Ludzie downvote pytanie nie znajdą jasne lub użyteczne. Ponieważ twoje pytanie wydaje się wystarczająco jasne, sądzę, że ktoś (nie ja) nie uznał tego za przydatny. – Manuel

+5

Nie ma takiego języka, jak C/C++ –

+1

Być może został odrzucony, ponieważ pytanie zostało zadane tutaj wiele razy wcześniej, na przykład http://stackoverflow.com/questions/1149764/delete-folder-with-items, a ludzie masz dość widzenia? Downvoter to nie ja, BTW. –

Odpowiedz

21

Chcesz napisać funkcję (funkcja rekursywna jest najłatwiejsza, ale może łatwo zabraknąć miejsca na stosie w głębokich katalogach), która wyliczy dzieci katalogu. Jeśli znajdziesz dziecko, które jest katalogiem, powtarzasz to. W przeciwnym razie usuniesz pliki wewnątrz. Kiedy skończysz, katalog jest pusty i możesz go usunąć przez syscall.

Aby wyliczyć katalogi w systemie Unix, można użyć opendir, readdir i closedir. Aby usunąć, użyj rmdir() w pustym katalogu (tj. Na końcu swojej funkcji, po usunięciu potomków) i unlink() w pliku. Należy zauważyć, że w wielu systemach element d_type w struct dirent nie jest obsługiwany; na tych platformach będziesz musiał użyć stat() i S_ISDIR(stat.st_mode), aby określić, czy dana ścieżka jest katalogiem.

W systemie Windows można użyć FindFirstFile()/FindNextFile() wyliczyć, RemoveDirectory() na pustych katalogów i DeleteFile() usunąć pliki.

Oto przykład, który może pracować na systemie Unix (całkowicie niesprawdzonych):

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

rozsądna odpowiedź, ale "łatwo zabrakło stackspace"? – peterchen

+0

@peterchen Tak. Widziałem, jak to się stało. Jest także łatwiejszy w systemie Windows niż POSIX, ponieważ 'WIN32_FIND_DATA' jest ogromny, podczas gdy' DIR * 'i' struct dirent * 'to tylko dwa wskaźniki .. – asveikau

+4

Pierwszy raz, kiedy to zrobiłem, nie sprawdzałem" .. " . I tak, wynikiem jest to, że program zmienia katalog aż do c :, a następnie zaczyna usuwać stamtąd! "Na szczęście", stało się to w pracy. :) –

1

Można użyć opendir i readdir czytać wpisy katalogów i rozłączyć, aby je usunąć.

0

unlink usunie plik.

remove spowoduje również usunięcie pliku, ale jest bardziej przenośny.

Możesz spróbować system("rm -r ./path"), jeśli pracujesz na systemie Linux, w przeciwnym razie istnieje funkcja kasowania rekursywnego interfejsu API systemu Windows.

+30

'system()' jest prawie zawsze złą odpowiedzią. – asveikau

+5

Ma swoje zastosowania, jest to szybkie i brudne rozwiązanie. – Xorlev

+0

jaka jest funkcja kasowania rekursywnego interfejsu API systemu Windows? – Manuel

13

Najprostszym sposobem na to jest z remove_all funkcji biblioteki Boost.Filesystem. Poza tym wynikowy kod będzie przenośny.

Jeśli chcesz napisać coś specyficznego dla Uniksa (rmdir) lub dla Windows (RemoveDirectory), będziesz musiał napisać funkcję, która usuwa rekursywnie podfile i podfoldery.

EDIT

Wygląda na to pytanie była already asked, w rzeczywistości ktoś już zalecana remove_all doładowania za. Więc proszę nie głosujcie na moją odpowiedź.

+2

brb Pobieranie biblioteki Boost.Filesystem na moim DOS wirtualnym pudełku do użycia na moim kompilatorze turbo c. – Dmitry

13

Wiele systemów uniksowych (Linux, BSD i OS X, co najmniej) ma funkcje fts do przeglądania katalogów. Aby rekurencyjnie usunąć katalog, po prostu przeprowadź najpierw konwersję głębi (bez podążania za dowiązaniami symbolicznymi) i usuń każdy odwiedzany plik.

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

Bardzo ładny przykładowy kod i wyjaśnienie - to powinna być zaakceptowana odpowiedź. –

+0

Interfejs 'fts' [nie jest dostępny w bibliotece muzycznej libl] (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

Otaczanie kodu z wyjaśnieniem poważnie poprawi odpowiedź. – zx485

7

Jeśli używasz systemu operacyjnego zgodnego z POSIX, można użyć nftw() do drzewa i usunąć plik przechodzenie (usuwa pliki lub katalogi). Jeśli jesteś w C++ i twój projekt wykorzystuje doładowanie, nie jest złym pomysłem użycie Boost.Filesystem zgodnie z sugestią Manuela.

W przykładzie kodu poniżej postanowiłem nie przechodzić dowiązania symboliczne i punkty montowania (tylko w celu uniknięcia wielkie usuwanie :)):

#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

Cieszę się, że ktoś wspomniał o 'ntfw'. :) –