Как удалить непустой каталог в C или С++? Есть ли какая-нибудь функция? rmdir удаляет только пустой каталог. Предоставьте способ, не используя внешнюю библиотеку.
Также скажите, как удалить файл на C или С++?
Как удалить непустой каталог в C или С++? Есть ли какая-нибудь функция? rmdir удаляет только пустой каталог. Предоставьте способ, не используя внешнюю библиотеку.
Также скажите, как удалить файл на C или С++?
Вы хотите написать функцию (рекурсивная функция проще всего, но может легко закончиться из стекового пространства в глубоких каталогах), которая будет перечислять дочерние элементы каталога. Если вы найдете ребенка, который является каталогом, вы рекурсируете на это. В противном случае вы удаляете файлы внутри. Когда вы закончите, каталог пуст, и вы можете удалить его с помощью 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;
}
Самый простой способ сделать это - remove_all в библиотеке Boost.Filesystem. Кроме того, полученный код будет переносимым.
Если вы хотите написать что-то конкретное для Unix (rmdir) или для Windows (RemoveDirectory), вам придется написать функцию, которая удаляет рекурсивные подфайлы и подпапки.
ИЗМЕНИТЬ
Похоже, этот вопрос был уже задан, на самом деле кто-то уже рекомендовал Boost remove_all. Поэтому, пожалуйста, не повышайте мой ответ.
Многие 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;
}
Если вы используете совместимую с 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;
}
Вы можете использовать opendir и readdir для чтения записей в каталоге и отсоединения для их удаления.
//======================================================
// 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;
}
unlink
удалит файл.
remove
также удалит файл, но будет более переносимым.
Вы можете попробовать system("rm -r ./path")
, если вы работаете в Linux, иначе там также будет рекурсивная функция удаления Windows API.