2011-12-21 17 views
22

Jak mogę skopiować katalog przy użyciu systemu plików Boost? Próbowałem boost :: filesystem :: copy_directory(), ale to tylko tworzy docelowy katalog i nie kopiuje zawartości.Jak skopiować katalog przy użyciu systemu plików doładowania?

+0

boost :: filesystem :: copy spowoduje skopiowanie katalogów lub plików. Możesz napisać rekursywny algorytm, który skopiuje katalog i pliki w jego obrębie. – nijansen

+1

Ah. Dziękuję Ci. Jestem zaskoczony, że nie jest to częścią systemu boost :: files. Również nie znalazłem żadnej dokumentacji na stronie internetowej biblioteki Boost, która mówi po angielsku, co właściwie robi funkcja copy_directory. – Ant

Odpowiedz

40
bool copyDir(
    boost::filesystem::path const & source, 
    boost::filesystem::path const & destination 
) 
{ 
    namespace fs = boost::filesystem; 
    try 
    { 
     // Check whether the function call is valid 
     if(
      !fs::exists(source) || 
      !fs::is_directory(source) 
     ) 
     { 
      std::cerr << "Source directory " << source.string() 
       << " does not exist or is not a directory." << '\n' 
      ; 
      return false; 
     } 
     if(fs::exists(destination)) 
     { 
      std::cerr << "Destination directory " << destination.string() 
       << " already exists." << '\n' 
      ; 
      return false; 
     } 
     // Create the destination directory 
     if(!fs::create_directory(destination)) 
     { 
      std::cerr << "Unable to create destination directory" 
       << destination.string() << '\n' 
      ; 
      return false; 
     } 
    } 
    catch(fs::filesystem_error const & e) 
    { 
     std::cerr << e.what() << '\n'; 
     return false; 
    } 
    // Iterate through the source directory 
    for(
     fs::directory_iterator file(source); 
     file != fs::directory_iterator(); ++file 
    ) 
    { 
     try 
     { 
      fs::path current(file->path()); 
      if(fs::is_directory(current)) 
      { 
       // Found directory: Recursion 
       if(
        !copyDir(
         current, 
         destination/current.filename() 
        ) 
       ) 
       { 
        return false; 
       } 
      } 
      else 
      { 
       // Found file: Copy 
       fs::copy_file(
        current, 
        destination/current.filename() 
       ); 
      } 
     } 
     catch(fs::filesystem_error const & e) 
     { 
      std:: cerr << e.what() << '\n'; 
     } 
    } 
    return true; 
} 

Zastosowanie:

copyDir(boost::filesystem::path("/home/nijansen/test"), boost::filesystem::path("/home/nijansen/test_copy")); (Unix)

copyDir(boost::filesystem::path("C:\\Users\\nijansen\\test"), boost::filesystem::path("C:\\Users\\nijansen\\test2")); (Windows)

O ile widzę, to najgorsze co może się zdarzyć, że nic się nie dzieje, ale wygrał Nic nie obiecuję! Używaj na własne ryzyko.

Należy pamiętać, że katalog, do którego kopiujesz, nie może istnieć. Jeśli katalogi w katalogu, który próbujesz skopiować, nie mogą zostać odczytane (pomyśl o zarządzaniu uprawnieniami), zostaną one pominięte, ale pozostałe powinny zostać skopiowane.

Aktualizacja

refactored funkcję odpowiednią do komentarzy. Ponadto funkcja zwraca teraz wynik pomyślny. Zwróci ona false, jeśli wymagania dla podanych katalogów lub katalogów w katalogu źródłowym nie są spełnione, ale nie w przypadku, gdy nie można skopiować pojedynczego pliku.

+7

Jeśli używasz C++, powinieneś użyć 'std :: cerr' zamiast' fprintf' i 'stderr'. A także, ponieważ jest to Boost.Filesystem, powinieneś użyć 'boost :: path' zamiast' std :: string'. –

+0

Dzięki za sugestie, poprawiłem funkcję odpowiednio. – nijansen

+3

Pamiętaj, że musisz uważać na to, co kopiujesz. Jeśli uruchomiłeś 'copyDir (boost :: filesystem :: path (". "), Boost :: filesystem :: path (" test "))', kopiuje się do momentu zakończenia, ponieważ długość ścieżki przekracza limit, lub skończy Ci się miejsce na dysku. – nijansen

6

Widzę tę wersję jako ulepszoną po wersji odpowiedzi @ nijansen. Wspiera także katalogi źródłowe i/lub docelowe, które mają być względne.

namespace fs = boost::filesystem; 

void copyDirectoryRecursively(const fs::path& sourceDir, const fs::path& destinationDir) 
{ 
    if (!fs::exists(sourceDir) || !fs::is_directory(sourceDir)) 
    { 
     throw std::runtime_error("Source directory " + sourceDir.string() + " does not exist or is not a directory"); 
    } 
    if (fs::exists(destinationDir)) 
    { 
     throw std::runtime_error("Destination directory " + destinationDir.string() + " already exists"); 
    } 
    if (!fs::create_directory(destinationDir)) 
    { 
     throw std::runtime_error("Cannot create destination directory " + destinationDir.string()); 
    } 

    for (const auto& dirEnt : fs::recursive_directory_iterator{sourceDir}) 
    { 
     const auto& path = dirEnt.path(); 
     auto relativePathStr = path.string(); 
     boost::replace_first(relativePathStr, sourceDir.string(), ""); 
     fs::copy(path, destinationDir/relativePathStr); 
    } 
} 

Główne różnice są wyjątki zamiast wartości zwracanej, stosowanie recursive_directory_iterator i boost::replace_first rozebrać wspólną część ścieżki iteratora, a opierając się na boost::filesystem::copy() aby zrobić dobry uczynek z różnych typów plików (zachowując dowiązania, na przykład).

+0

+1 dla preferowania wyjątków w porównaniu z wartościami zwracanymi przez boolowskie. Również relativePathStr można obliczyć za pomocą path.lexically_relative (sourceDir), który może być łatwiejszy do odczytania niż boost :: replace_first. – Philippe

2

Już nie potrzebujesz doładowania dla tego zadania, ponieważ jego biblioteka systemów plików została dołączona do std C++ z np. dodanie std::filesystem::copy

#include <exception> 
#include <filesystem> 
namespace fs = std::filesystem; 

int main() 
{ 
    fs::path source = "path/to/source/folder"; 
    fs::path target = "path/to/target/folder"; 

    try { 
     fs::copy(source, target, fs::copy_options::recursive); 
    } 
    catch (std::exception& e) { // Not using fs::filesystem_error since std::bad_alloc can throw too. 
     // Handle exception or use error code overload of fs::copy. 
    } 
} 

Zobacz także std::filesystem::copy_options.

+0

Wielkie dzięki Roi. Mam nadzieję, że odwiedzający zauważą twoją odpowiedź. – Ant

+0

Należy zauważyć, że dodano to do ISO C++ w C++ 17. –

Powiązane problemy