10.11. Удаление каталога

10.11. Удаление каталога

Проблема

Требуется удалить каталог, причем эта операция должна быть переносимой, т.е. в ней не должен использоваться специфичный для конкретной ОС программный интерфейс.

Решение

На большинстве платформ вы сможете воспользоваться системным вызовом rmdir, который входит в состав большинства компиляторов и содержится в заголовочных файлах C-функций. Стандартными средствами C++ нельзя обеспечить переносимый способ удаления каталога. Вызов rmdir имеет разный вид в различных ОС, но тем не менее вы можете его использовать для удаления каталога. См. Пример 10.17, в котором приводится короткая программа по удалению каталога.

Пример 10.17. Удаление каталога

#include <iostream>

#include <direct.h>

using namespace std;

int main(int argc, char** argv) {

 if (argc < 2) {

  cerr << "Usage: " << argv[0] << " [dir name]" << endl;

  return(EXIT_FAILURE);

 }

 if (rmdir(argv[1]) == -1) { // Удалить каталог

  cerr << "Error: " << strerror(errno) << endl;

  return(EXIT_FAILURE);

 }

}

Обсуждение

Сигнатура rmdir совпадает в большинстве ОС, однако объявляется эта функция в разных заголовочных файлах. В Windows она объявляется в <direct.h>, а в Unix — в <unistd.h>. Она принимает один параметр (имя каталога) и возвращает -1, если возникла ошибка, устанавливая в errno соответствующий номер ошибки. Вы можете получить зависящую от реализации текстовую строку ошибки, вызывая strerror или perror.

Если целевой каталог не пустой, rmdir завершится с ошибкой. Для просмотра списка содержимого каталога, перечисления его элементов для их удаления см. рецепт 10.12.

Если вам требуется обеспечить переносимость, не следует самому писать операторы #ifdef, заключая в них специфичные для конкретной ОС функции, — лучше воспользоваться библиотекой Boost Filesystem. В библиотеке Boost Filesystem используется концепция пути для ссылки на файл или каталог, а пути можно удалять с помощью одной функции — remove.

Функция removeRecurse из примера 10.18 рекурсивно удаляет каталог и все его содержимое. Наиболее важной ее частью является функция remove (которая на самом деле является функцией boost::filesystem::remove, а не стандартной библиотечной функцией). Она принимает путь path в качестве аргумента и удаляет его, если это файл или пустой каталог, но она не удаляет каталог, если он содержит файлы.

Пример 10.18. Удаление каталога средствами Boost

#include <iostream>

#include <string>

#include <cstdlib>

#include <boost/filesystem/operations.hpp>

#include <boost/filesystem/fstream.hpp>

using namespace std;

using namespace boost::filesystem;

void removeRecurse(const path& p) {

 // Сначала удалить содержимое каталога

 directory_iterator end;

 for (directory_iterator it(p); it != end; ++it) {

  if (is_directory(*it)) {

   removeRecurse(*it);

  } else {

   remove(*it);

  }

 }

 // Затем удалить сам каталог

 remove(p);

}

int main(int argc, char** argv) {

 if (argc != 2) {

  cerr << "Usage: " << argv[0] << " [dir name] ";

  return(EXIT_FAILURE);

 }

 path thePath = system_complete(path(argv[1], native));

 if (!exists(thePath)) {

  cerr << "Error: the directory " << thePath.string()

   << " does not exist. ";

  return(EXIT_FAILURE);

 }

 try {

  removeRecurse(thePath);

 } catch (exception& e) {

  cerr << e.what() << endl;

  return(EXIT_FAILURE);

 }

 return(EXIT_SUCCESS);

}

Программный код, обеспечивающий просмотр содержимого каталога, требует некоторых пояснений, и это является темой рецепта 10.12.

Библиотека Boost Filesystem достаточно удобна, однако следует помнить, что формально она не является стандартом, и поэтому нет гарантии, что она будет работать в любой среде. Если вы посмотрите на исходный код библиотеки Boost Filesystem, вы увидите, что фактически она компилирует системные вызовы, специфичные для целевой платформы. Если вас не волнует переносимость, используйте программный интерфейс файловой системы вашей ОС, который, вполне вероятно, обладает большей гибкостью.

Смотри также

Рецепт 10.12.