How to recursively copy files and directories
Asked Answered
K

2

15

With C++, is it possible to recursively copy files and directories from one path to another

  • without having to use any additional libraries?
  • and with platform independent functions?

Considering the following filesystem

src/fileInRoot
src/sub_directory/
src/sub_directory/fileInSubdir

I want to copy

  1. all files and directories or
  2. certain files and directories

from src to another directory target.


I've created a new question since the questions I've found are platform specific and don't include filtering:

Kaohsiung answered 19/7, 2018 at 20:44 Comment(3)
You should use Operating System API. The operating system is optimized for copying files, and creating directories.Nicolina
@ThomasMatthews Have you knowledge about performance measurements which compare OS API calls to e.g. std::filesystem in Linux/Windows? (my credo is to never sacrifice code readability based on assumptions or minor performance gains that are uninteresting for the use case, i.e. their impact does not affect user experience)Kaohsiung
It’s should be noted that modern window still supports Linux posix api Carried over from window NT though usually you need to use a define and ifdef _WIN32. to add the extra underscore that Microsoft likes to add to posix function to make them slightly different under window...Verbenia
K
28

Yes, it is possible to copy a complete directory structure using nothing else than std C++ ... beginning with C++17 and its std::filesystem which includes std::filesystem::copy.

  1. Copying all files can be done using copy_options::recursive:
// Recursively copies all files and folders from src to target and overwrites existing files in target.
void CopyRecursive(const fs::path& src, const fs::path& target) noexcept
{
    try
    {
        fs::copy(src, target, fs::copy_options::overwrite_existing | fs::copy_options::recursive);
    }
    catch (std::exception& e)
    {
        std::cout << e.what();
    }
}
  1. To copy a certain subset of files using a filter, recursive_directory_iterator can be utilized:
// Recursively copies those files and folders from src to target which matches
// predicate, and overwrites existing files in target.
void CopyRecursive(const fs::path& src, const fs::path& target,
                    const std::function<bool(fs::path)>& predicate /* or use template */) noexcept
{
    try
    {
        for (const auto& dirEntry : fs::recursive_directory_iterator(src))
        {
            const auto& p = dirEntry.path();
            if (predicate(p))
            {
                // Create path in target, if not existing.
                const auto relativeSrc = fs::relative(p, src);
                const auto targetParentPath = target / relativeSrc.parent_path();
                fs::create_directories(targetParentPath);

                // Copy to the targetParentPath which we just created.
                fs::copy(p, targetParentPath, fs::copy_options::overwrite_existing);
            }
        }
    }
    catch (std::exception& e)
    {
        std::cout << e.what();
    }
}

When calling the second method like

#include <filesystem>
#include <iostream>
#include <functional>
namespace fs = std::filesystem;

int main()
{
    const auto root = fs::current_path();
    const auto src = root / "src";
    const auto target = root / "target";

    // Copy only those files which contain "Sub" in their stem.
    const auto filter = [](const fs::path& p) -> bool
    {
        return p.stem().generic_string().find("Sub") != std::string::npos;
    };
    CopyRecursive(src, target, filter);
}

and the given filesystem is in the working directory of the process, then the result is

target/sub_directory/
target/sub_directory/fileInSubdir

You could also pass the copy_options as parameter to CopyRecursive() for even more flexibility.


A list of some of the functions from std::filesystem which were used above:


For production code, I recommend to pull the error handling out of the utility functions. For error handling, std::filesystem provides two methods:

  1. exceptions with std::exception/std::filesystem::filesystem_error
  2. and error codes with std::error_code.

Also take into consideration, that std::filesystem might not be available on all platforms

The filesystem library facilities may be unavailable if a hierarchical file system is not accessible to the implementation, or if it does not provide the necessary capabilities. Some features may not be available if they are not supported by the underlying file system (e.g. the FAT filesystem lacks symbolic links and forbids multiple hardlinks). In those cases, errors must be reported.

Kaohsiung answered 19/7, 2018 at 20:44 Comment(0)
H
0

Roi Danton's method is the best, but consideration that std::filesystem might not be available on all platforms. Maybe this could be an alternative way.

#include <fstream>
#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

//function copy files
void cpFile(const fs::path & srcPath,
  const fs::path & dstPath) {

  std::ifstream srcFile(srcPath, std::ios::binary);
  std::ofstream dstFile(dstPath, std::ios::binary);

  if (!srcFile || !dstFile) {
    std::cout << "Failed to get the file." << std::endl;
    return;
  }

  dstFile << srcFile.rdbuf();

  srcFile.close();
  dstFile.close();
}

//function create new directory
void cpDirectory(const fs::path & srcPath,
  const fs::path & dstPath) {

  fs::create_directories(dstPath);

  for (const auto & entry: fs::directory_iterator(srcPath)) {
    const fs::path & srcFilePath = entry.path();
    const fs::path & dstFilePath = dstPath / srcFilePath.filename();
    //if directory then create new folder
    if (fs::is_directory(srcFilePath)) {
      cpDirectory(srcFilePath, dstFilePath);
    } else {
      cpFile(srcFilePath, dstFilePath);
    }
  }
}

int main() {
  const fs::path srcPath = root / "src";
  const fs::path dstPath = root / "target";

  // Copy only those files which contain "Sub" in their stem.
  cpDirectory(srcPath, dstPath);

  return 0;

}
Hodometer answered 23/6, 2023 at 3:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.