Подтвердить что ты не робот

Удаление непустого каталога программно в C или С++

Как удалить непустой каталог в C или С++? Есть ли какая-нибудь функция? rmdir удаляет только пустой каталог. Предоставьте способ, не используя внешнюю библиотеку.

Также скажите, как удалить файл на C или С++?

4b9b3361

Ответ 1

Вы хотите написать функцию (рекурсивная функция проще всего, но может легко закончиться из стекового пространства в глубоких каталогах), которая будет перечислять дочерние элементы каталога. Если вы найдете ребенка, который является каталогом, вы рекурсируете на это. В противном случае вы удаляете файлы внутри. Когда вы закончите, каталог пуст, и вы можете удалить его с помощью syscall.

Чтобы перечислить каталоги в Unix, вы можете использовать opendir, readdir и closedir. Для удаления используйте rmdir() в пустом каталоге (т.е. В конце вашей функции, после удаления дочерних элементов) и unlink() в файле. Обратите внимание, что во многих системах член d_type в struct dirent не поддерживается; на этих платформах вам нужно будет использовать stat() и S_ISDIR(stat.st_mode), чтобы определить, является ли данный путь каталогом.

В Windows вы будете использовать FindFirstFile()/FindNextFile() для перечисления, RemoveDirectory() в пустых каталогах и DeleteFile() для удаления файлов.

Вот пример, который может работать в Unix (полностью непроверенный):

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

Ответ 2

Самый простой способ сделать это - remove_all в библиотеке Boost.Filesystem. Кроме того, полученный код будет переносимым.

Если вы хотите написать что-то конкретное для Unix (rmdir) или для Windows (RemoveDirectory), вам придется написать функцию, которая удаляет рекурсивные подфайлы и подпапки.

ИЗМЕНИТЬ

Похоже, этот вопрос был уже задан, на самом деле кто-то уже рекомендовал Boost remove_all. Поэтому, пожалуйста, не повышайте мой ответ.

Ответ 3

Многие unix-подобные системы (Linux, BSD и OS X, по крайней мере) имеют функции fts для обхода каталога. Чтобы рекурсивно удалить каталог, просто выполните обход глубины (без следующих символических ссылок) и удалите каждый посещенный файл.

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

Ответ 4

Если вы используете совместимую с POSIX ОС, вы можете использовать nftw() для обхода дерева файлов и удаления (удаляет файлы или каталоги). Если вы находитесь на С++, и ваш проект использует boost, неплохо было бы использовать Boost.Filesystem, предложенную Мануэлем.

В приведенном ниже примере кода я решил не пересекать символические ссылки и точки монтирования (просто чтобы избежать большого удаления:)):

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

Ответ 5

Вы можете использовать opendir и readdir для чтения записей в каталоге и отсоединения для их удаления.

Ответ 6

//======================================================
// 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;
}

Ответ 7

unlink удалит файл.

remove также удалит файл, но будет более переносимым.

Вы можете попробовать system("rm -r ./path"), если вы работаете в Linux, иначе там также будет рекурсивная функция удаления Windows API.