2011-12-21 12 views
22

Come posso copiare una directory usando Boost Filesystem? Ho provato boost :: filesystem :: copy_directory() ma questo crea solo la directory di destinazione e non copia i contenuti.Come posso copiare una directory usando Boost Filesystem

+0

boost :: filesystem :: copy copia le directory oi file allo stesso modo. È possibile scrivere un algoritmo ricorsivo che copia la directory e i file all'interno di quello. – nijansen

+1

Ah. Grazie. Sono sorpreso che questo non fa parte di boost :: filesystem. Inoltre, non sono riuscito a trovare alcuna documentazione nel sito web della libreria Boost che dicesse in inglese cosa fa effettivamente la funzione copy_directory. – Ant

risposta

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

Usage:

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)

Per quanto vedo, il peggio che può succedere è che non succede nulla, ma ho vinto Prometto qualcosa! Utilizzare a proprio rischio.

Si prega di notare che la directory su cui si sta copiando non deve esistere. Se le directory all'interno della directory che stai tentando di copiare non possono essere lette (si pensi alla gestione dei diritti), verranno saltate, ma le altre dovrebbero essere ancora copiate.

Aggiornamento

riscritta la funzione relativa ai commenti. Inoltre, la funzione ora restituisce un risultato positivo. Restituirà false se i requisiti per le directory specificate o qualsiasi directory all'interno della directory di origine non sono soddisfatti, ma non se un singolo file non può essere copiato.

+7

Se usi C++, dovresti usare 'std :: cerr' invece di' fprintf' e 'stderr'. Inoltre, poiché questo è Boost.Filesystem, dovresti usare 'boost :: path' invece di' std :: string'. –

+0

Grazie per i suggerimenti, ho migliorato la funzione di conseguenza. – nijansen

+3

Nota che devi ancora stare attento a cosa stai copiando dove. Se hai eseguito 'copyDir (boost :: filesystem :: path (". "), Boost :: filesystem :: path (" test "))', si copierà fino a quando non verrà terminato perché la lunghezza del percorso supera il limite, o si esaurisce lo spazio su disco. – nijansen

6

Vedo questa versione come una versione migliorata della risposta di @ nijansen. Supporta inoltre che le directory di origine e/o destinazione siano relative.

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

Le principali differenze sono eccezioni al posto dei valori di ritorno, l'uso di recursive_directory_iterator e boost::replace_first per mettere a nudo la parte comune del percorso iteratore, e basandosi su boost::filesystem::copy() di fare la cosa giusta, con diversi tipi di file (conservando i collegamenti simbolici, per esempio).

+0

+1 per preferire le eccezioni rispetto ai valori di ritorno booleani. Inoltre, relativoPathStr può essere calcolato utilizzando path.lexically_relative (sourceDir), che può essere più semplice da leggere di boost :: replace_first. – Philippe

2

Non hai più bisogno di boost per quel compito dato che la sua libreria di file system è stata inclusa in std C++ con ad es. l'aggiunta di 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. 
    } 
} 

Vedere anche std::filesystem::copy_options.

+0

Molte grazie, Roi. Spero che i visitatori notino la tua risposta. – Ant

+0

Nota: questo è stato aggiunto a ISO C++ in C++ 17. –

Problemi correlati