【问题标题】:How can I copy a directory using Boost Filesystem如何使用 Boost Filesystem 复制目录
【发布时间】:2012-01-25 11:43:29
【问题描述】:

如何使用 Boost Filesystem 复制目录? 我试过 boost::filesystem::copy_directory() 但这只会创建目标目录而不复制内容。

【问题讨论】:

  • boost::filesystem::copy 将复制目录或文件。您可以编写一个递归算法,使用它复制目录和文件。
  • 啊。谢谢你。我很惊讶这不是 boost::filesystem 的一部分。此外,我在 Boost 库网站上找不到任何用英文说明函数 copy_directory 实际作用的文档。

标签: c++ boost filesystems directory copy


【解决方案1】:

这是我使用的基于 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() 调用的有效替代品

【讨论】:

    【解决方案2】:

    从 C++17 开始,您不再需要进行此操作的 boost,因为文件系统已添加到标准中。

    使用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

    【讨论】:

    • 非常感谢罗伊。我希望访客能注意到您的回答。
    • 请注意,这是在 C++ 17 中添加到 ISO C++ 中的。
    【解决方案3】:

    我将此版本视为@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_iteratorboost::replace_first剥离迭代器路径的公共部分,依靠boost::filesystem::copy()对不同的文件类型做正确的事情(保留例如符号链接)。

    【讨论】:

    • +1 表示优先于布尔返回值的异常。此外,relativePathStr 可以使用 path.lexically_relative(sourceDir) 计算,这可能比 boost::replace_first 更易于阅读。
    【解决方案4】:
    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)

    据我所知,最坏的情况是什么都没有发生,但我不会承诺任何事情!使用风险自负。

    请注意,您要复制到的目录不得存在。如果您尝试复制的目录中的目录无法读取(想想权限管理),它们将被跳过,但仍应复制其他目录。

    更新

    重构了与 cmets 相关的函数。此外,该函数现在返回成功结果。如果不满足给定目录或源目录中任何目录的要求,它将返回 false,但如果无法复制单个文件,则不会返回。

    【讨论】:

    • 如果你使用 C++,应该使用 std::cerr 而不是 fprintfstderr。而且,由于这是 Boost.Filesystem,你应该使用 boost::path 而不是 std::string
    • 感谢您的建议,我对功能进行了相应的改进。
    • 请注意,您仍然必须小心复制的内容。如果你运行copyDir(boost::filesystem::path("."), boost::filesystem::path("test")),它将自我复制,直到它因为路径长度超过限制而被终止,或者你的磁盘空间不足。
    • 非常感谢 nijansen,这非常适合我的工作。我正在将一个定义明确且已知的文件夹复制到另一个位置,因此我无需担心任何特殊情况。我仍然对 copy_directory 不复制内容或至少有复制内容的选项感到惊讶。
    • 您应该使用 / 运算符,而不是执行 dest.string() + "/"。 dest / current.filename()
    猜你喜欢
    • 1970-01-01
    • 2015-11-08
    • 1970-01-01
    • 2013-04-04
    • 2011-06-14
    • 2015-03-26
    • 1970-01-01
    • 2016-06-02
    • 2011-04-25
    相关资源
    最近更新 更多