web-dev-qa-db-ja.com

Boost Filesystemを使用してディレクトリをコピーする方法

Boost Filesystemを使用してディレクトリをコピーするにはどうすればよいですか? boost :: filesystem :: copy_directory()を試しましたが、ターゲットディレクトリが作成されるだけで、内容はコピーされません。

28
Ant
_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;
}
_

使用法:

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)

私の知る限り、起こり得る最悪の事態は何も起こらないことですが、何も約束しません!自己責任。

コピー先のディレクトリが存在していてはいけないことに注意してください。コピーしようとしているディレクトリ内のディレクトリを読み取ることができない場合(権利管理を考えると)、それらはスキップされますが、他のディレクトリは引き続きコピーする必要があります。

更新

コメントに応じて関数をリファクタリングしました。さらに、関数は成功結果を返すようになりました。指定されたディレクトリまたはソースディレクトリ内のディレクトリの要件が満たされていない場合はfalseを返しますが、単一のファイルをコピーできなかった場合は返しません。

44
nijansen

C++ 17以降では、ファイルシステムが標準に追加されているため、この操作でブーストを行う必要はありません。

使用する - 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.
    }
}

参照 std::filesystem::copy_options

13
Roi Danton

このバージョンは、@ nijansenの回答のバージョンを改良したものだと思います。また、ソースディレクトリや宛先ディレクトリを相対的にサポートします。

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

主な違いは、戻り値の代わりに例外があり、_recursive_directory_iterator_と_boost::replace_first_を使用してイテレーターパスの共通部分を取り除き、boost::filesystem::copy()を使用して正しいことを行うさまざまなファイルタイプ(シンボリックリンクを保持するなど)。

11
Doineann

これは、Doineannのコードに基づいて私が使用している非Boostバージョンです。 std :: filesystemを使用していますが、単純なfs::copy(src, dst, fs::copy_options::recursive);を使用できませんでした。ループ内のファイル拡張子によってコピーされるファイルをフィルター処理したかったからです。

_void CopyRecursive(fs::path src, fs::path dst)
{
    //Loop through all the dirs
    for (auto dir : fs::recursive_directory_iterator(src))
    {
        //copy the path's string to store relative path string
        std::wstring relstr = dir.path().wstring();

        //remove the substring matching the src path
        //this leaves only the relative path
        relstr.erase(0, std::wstring(src).size());

        //combine the destination root path with relative path
        fs::path newFullPath = dst / relstr;

        //Create dir if it's a dir
        if (fs::is_directory(newFullPath))
        {
            fs::create_directory(newFullPath);
        }

        //copy the files
        fs::copy(dir.path(), newFullPath, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
    }
}
_

relstr.erase(0, std::wstring(src).size());は、他の回答で使用されているboost :: replace_first()呼び出しの動作するBoostなしの置換です。

0
GuidedHacking