web-dev-qa-db-fra.com

Supprimer un répertoire non vide par programme en C ou C++

Comment supprimer un répertoire non vide en C ou C++? Y a-t-il une fonction? rmdir ne supprime que le répertoire vide. S'il vous plaît fournir un moyen sans utiliser aucune bibliothèque externe. 

Dites-moi aussi comment supprimer un fichier en C ou C++?

43
avd

Vous voulez écrire une fonction (une fonction récursive est la plus simple, mais peut facilement manquer d'espace de pile sur des répertoires profonds) qui énumérera les enfants d'un répertoire. Si vous trouvez un enfant qui est un répertoire, vous récidivez à ce sujet. Sinon, vous supprimez les fichiers à l'intérieur. Lorsque vous avez terminé, le répertoire est vide et vous pouvez le supprimer via l'appel système.

Pour énumérer les répertoires sous Unix, vous pouvez utiliser opendir, readdir et closedir. Pour supprimer, utilisez rmdir() dans un répertoire vide (c'est-à-dire à la fin de votre fonction, après la suppression des enfants) et unlink() dans un fichier. Notez que sur de nombreux systèmes, le membre d_type dans struct dirent n'est pas pris en charge. sur ces plates-formes, vous devrez utiliser stat() et S_ISDIR(stat.st_mode) pour déterminer si un chemin donné est un répertoire.

Sous Windows, vous utiliserez FindFirstFile()/FindNextFile() pour énumérer, RemoveDirectory() dans des répertoires vides et DeleteFile() pour supprimer des fichiers.

Voici un exemple qui pourrait fonctionner sous Unix (complètement non testé):

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;
}
28
asveikau

De nombreux systèmes de type Unix (Linux, les BSD et OS X, à tout le moins) ont les fonctions fts pour parcourir les répertoires. Pour supprimer de manière récursive un répertoire, effectuez une traversée en profondeur d'abord (sans suivre les liens symboliques) et supprimez tous les fichiers visités.

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;
}
14
Andrew Gunnerson

La méthode la plus simple consiste à utiliser la commande remove_all de la bibliothèque Boost.Filesystem. En outre, le code résultant sera portable.

Si vous voulez écrire quelque chose de spécifique pour Unix (rmdir) ou pour Windows (RemoveDirectory), vous devrez écrire une fonction qui supprime récursivement les sous-fichiers et les sous-dossiers.

MODIFIER

On dirait que cette question était déjà posée , en fait, quelqu'un a déjà recommandé le programme remove_all de Boost. Alors s'il vous plaît ne pas upvote ma réponse.

14
Manuel

Si vous utilisez un système d'exploitation compatible POSIX, vous pouvez utiliser nftw() pour parcourir et supprimer des fichiers (supprime des fichiers ou des répertoires). Si vous êtes en C++ et que votre projet utilise boost, ce n'est pas une mauvaise idée d'utiliser Boost.Filesystem comme suggéré par Manuel.

Dans l'exemple de code ci-dessous, j'ai décidé de ne pas parcourir les liens symboliques ni les points de montage (juste pour éviter une suppression massive :)):

#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;
    }
    return 0;
}

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;
}
10
marcmagransdeabril

Vous pouvez utiliser opendir et readdir pour lire les entrées du répertoire et unlink pour les supprimer.

1
diciu

C++ 17 a <experimental\filesystem> qui est basé sur la version boostée.

Utilisez std :: experimental :: filesystem :: remove_all pour supprimer récursivement.

Si vous avez besoin de plus de contrôle, essayez std :: experimental :: filesystem :: recursive_directory_iterator .

Vous pouvez également écrire votre propre récurrence avec la version non-resursive de l'itérateur.

namespace fs = std::experimental::filesystem;
void IterateRecursively(fs::path path)
{
  if (fs::is_directory(path))
  {
    for (auto & child : fs::directory_iterator(path))
      IterateRecursively(child.path());
  }

  std::cout << path << std::endl;
}
1
greedy52

unlink supprimera un fichier.

remove supprimera également un fichier mais sera plus portable.

Vous pouvez essayer system("rm -r ./path") si vous travaillez sous Linux, sinon il existe également une fonction de suppression récursive des API Windows.

0
Xorlev

Comment supprimer un dossier non vide en utilisant unlinkat () dans c?

Voici mon travail à ce sujet:

    /*
     * Program to erase the files/subfolders in a directory given as an input
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dirent.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    void remove_dir_content(const char *path)
    {
        struct dirent *de;
        char fname[300];
        DIR *dr = opendir(path);
        if(dr == NULL)
        {
            printf("No file or directory found\n");
            return;
        }
        while((de = readdir(dr)) != NULL)
        {
            int ret = -1;
            struct stat statbuf;
            sprintf(fname,"%s/%s",path,de->d_name);
            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
                        continue;
            if(!stat(fname, &statbuf))
            {
                if(S_ISDIR(statbuf.st_mode))
                {
                    printf("Is dir: %s\n",fname);
                    printf("Err: %d\n",ret = unlinkat(dirfd(dr),fname,AT_REMOVEDIR));
                    if(ret != 0)
                    {
                        remove_dir_content(fname);
                        printf("Err: %d\n",ret = unlinkat(dirfd(dr),fname,AT_REMOVEDIR));
                    }
                }
                else
                {
                    printf("Is file: %s\n",fname);
                    printf("Err: %d\n",unlink(fname));
                }
            }
        }
        closedir(dr);
    }
    void main()
    {
        char str[10],str1[20] = "../",fname[300]; // Use str,str1 as your directory path where it's files & subfolders will be deleted.
        printf("Enter the dirctory name: ");
        scanf("%s",str);
        strcat(str1,str);
        printf("str1: %s\n",str1);
        remove_dir_content(str1); //str1 indicates the directory path
    }
0
Samson Praneeth
//======================================================
// 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
Bill Moore