目录

C++与python文件系统对比


C++17 和 python 中好用的文件操作 | filesystem | os | shutil

C++ 17 python 功能
filesystem::path::is_absolute() os.path.isabs() 判断是否为绝对路径
filesystem::path::parent_path() os.path.dirname() 路径分割
filesystem::path::filename() os.path.basename() 路径分割
filesystem::operator/() os.path.join() 路径拼接
filesystem::current_path() os.getcwd() 获取当前路径
filesystem::directory_iterator os.listdir() 返回指定目录下的所有文件/文件夹
filesystem::recursive_directory_iterator os.walk() 递归返回指定目录下的所有文件/文件夹
filesystem::exists() os.path.exists() 判断路径是否存在
filesystem::is_regular_file() os.path.isfile() 判断路径是文件还是目录
filesystem::is_directory() os.path.isdir() 判断路径是文件还是目录
filesystem::absolute() os.path.abspath() 返回绝对路径
filesystem::copy_file() shutil.copyfile() 文件拷贝
filesystem::remove() os.remove() 文件删除
filesystem::copy() shutil.copytree 路径拷贝
filesystem::remove_all shutil.rmtree() 路径删除

filesystem::path vs. os.path

  1. filesystem::path是一个类,里面封装了很多方法,我们通过实例化之后直接调用方法。
  2. os.path是一个模块,里面有很多函数,可以直接调用。

判断是否为绝对路径

什么是绝对路径?我个人的理解是从根目录开始的就是绝对路径,例如/usr/localC:\\Users,其余都是相对路径。可以发现,在不同操作系统中路径的分割符是不同的。同时在相对路径中./../有特殊含义,./表示当前目录,../表示上一层目录,相应地,../../就是上两层目录。

1.filesystem::path中提供了判断是否为绝对路径/相对路径方法。

  _LIBCPP_INLINE_VISIBILITY bool is_absolute() const {
    return has_root_directory();
  }
  _LIBCPP_INLINE_VISIBILITY bool is_relative() const { return !is_absolute(); }

可以发现,判断相对路径的结果就是绝对路径取反。

void eg1_1() {
    /*判断是否为绝对路径*/
    // std::filesystem::path abs_path = "C:\\Users";
    std::filesystem::path abs_path = "/usr/local";  // 注意,实例化path的时候可以直接用等号
    std::cout << "abs_path.is_absolute() : " << abs_path.is_absolute() << std::endl;
    std::cout << "abs_path.is_relative() : " << abs_path.is_relative() << std::endl;

    std::filesystem::path rel_path = "../";
    std::cout << "rel_path.is_absolute() : " << rel_path.is_absolute() << std::endl;
    std::cout << "rel_path.is_relative() : " << rel_path.is_relative() << std::endl;
}
abs_path.is_absolute() : 1
abs_path.is_relative() : 0
rel_path.is_absolute() : 0
rel_path.is_relative() : 1
  1. os.path中提供了isabs()函数用于判断是否为绝对路径。
def eg1_1():
    """判断是否为绝对路径"""
    # abs_path = "C:\\Users"
    abs_path = "/usr/local"
    print("os.path.isabs({}) : {}".format(abs_path, os.path.isabs(abs_path)))
    rel_path = "../"
    print("os.path.isabs({}) : {}".format(rel_path, os.path.isabs(rel_path)))
os.path.isabs(/usr/local) : True
os.path.isabs(../) : False

路径分割

  1. filesystem::path中提供了路径分割的方法。
  // decomposition
  _LIBCPP_INLINE_VISIBILITY path root_name() const {
    return string_type(__root_name());
  }
  _LIBCPP_INLINE_VISIBILITY path root_directory() const {
    return string_type(__root_directory());
  }
  _LIBCPP_INLINE_VISIBILITY path root_path() const {
    return root_name().append(string_type(__root_directory()));
  }
  _LIBCPP_INLINE_VISIBILITY path relative_path() const {
    return string_type(__relative_path());
  }
  _LIBCPP_INLINE_VISIBILITY path parent_path() const {
    return string_type(__parent_path());
  }
  _LIBCPP_INLINE_VISIBILITY path filename() const {
    return string_type(__filename());
  }
  _LIBCPP_INLINE_VISIBILITY path stem() const { return string_type(__stem()); }
  _LIBCPP_INLINE_VISIBILITY path extension() const {
    return string_type(__extension());
  }
void eg1_2() {
    /*路径分割*/
    std::filesystem::path path = "../test_dir/1.txt";
    std::cout << "path.relative_path() : " << path.relative_path() << std::endl;
    std::cout << "path.parent_path() : " << path.parent_path() << std::endl;
    std::cout << "path.filename() : " << path.filename() << std::endl;
    std::cout << "path.stem() : " << path.stem() << std::endl;
    std::cout << "path.extension() : " << path.extension() << std::endl;
}
path.relative_path() : "../test_dir/1.txt"
path.parent_path() : "../test_dir"
path.filename() : "1.txt"
path.stem() : "1"
path.extension() : ".txt"
  1. os.path中提供了分割函数split()以及dirname()basename()
def eg1_2():
    """路径分割"""
    path = "../test_dir/1.txt"
    print("os.path.split(path) : {}".format(os.path.split(path)))
    print("os.path.dirname(path) : {}".format(os.path.dirname(path)))
    print("os.path.basename(path) : {}".format(os.path.basename(path)))
os.path.split(path) : ('../test_dir', '1.txt')
os.path.dirname(path) : ../test_dir
os.path.basename(path) : 1.txt

路径拼接

  1. filesystem::path中重载了符号//=
  friend _LIBCPP_INLINE_VISIBILITY path operator/(const path& __lhs,
                                                  const path& __rhs) {
    path __result(__lhs);
    __result /= __rhs;
    return __result;
  }
void eg1_3() {
    /*路径拼接*/
    std::filesystem::path path = "../test_dir";
    std::filesystem::path txt1_path = path / "1.txt";
    std::filesystem::path txt2_path = path / "1_dir" / "2.txt";
    std::cout << "txt1_path : " << txt1_path << std::endl;
    std::cout << "txt2_path : " << txt2_path << std::endl;
    path /= "1.txt";
    std::cout << "path : " << path << std::endl;
}
txt1_path : "../test_dir/1.txt"
txt2_path : "../test_dir/1_dir/2.txt"
path : "../test_dir/1.txt"
  1. os.path中提供了join()函数。
def eg1_3():
    """路径拼接"""
    path = "../test_dir"
    txt1_path = os.path.join(path, "1.txt")
    txt2_path = os.path.join(path, "1_dir", "2.txt")
    print("txt1_path : {}".format(txt1_path))
    print("txt2_path : {}".format(txt2_path))
txt1_path : ../test_dir/1.txt
txt2_path : ../test_dir/1_dir/2.txt
  1. 注意,不同操作系统的分隔符不同,所以在Windows中运行结果如下。
txt1_path : ../test_dir\1.txt
txt2_path : ../test_dir\1_dir\2.txt

filesystem vs. os

获取当前工作目录

  1. filesystem提供了获取当前路径的函数current_path(),注意返回的是绝对路径。
void eg2_1() {
    /*获取当前路径*/
    std::filesystem::path current_path = std::filesystem::current_path();
    std::cout << "current_path : " << current_path << std::endl;
}
current_path : "/Users/xxx/Github/intro_to_C-python/xxx/cmake-build-debug"
  1. os提供了函数getcwd()
def eg2_1():
    """获取当前路径"""
    current_path = os.getcwd()
    print("current_path : {}".format(current_path))
current_path : /Users/xxx/Github/intro_to_C-python/xxx

这里先展示一下目录树,方便理解后边的例子。

└── test_dir
    ├── 1.txt
    └── 1_dir
        ├── 2.txt
        └── empty_dir

返回指定目录下的所有文件/文件夹

  1. filesystem的类directory_iterator可以实现该功能。
void eg2_2() {
    /*返回指定目录下的所有文件/文件夹*/
    std::filesystem::directory_iterator iter("../test_dir");
    for(const auto &i : iter) {
        std::cout << i.path() <<std::endl;
    }
}
"../test_dir/1.txt"
"../test_dir/1_dir"
  1. os中提供了函数listdir()
def eg2_2():
    """返回指定目录下的所有文件/文件夹"""
    for i in os.listdir("./test_dir"):
        print(i)
1.txt
1_dir

递归返回指定目录下的所有文件/文件夹

  1. filesystem的类recursive_directory_iterator可以实现该功能。
void eg2_3() {
    /*递归返回指定目录下的所有文件/文件夹*/
    std::filesystem::recursive_directory_iterator iter("../test_dir");
    for(const auto &i : iter) {
        std::cout << i.path() <<std::endl;
    }
}
"../test_dir/1.txt"
"../test_dir/1_dir"
"../test_dir/1_dir/empty_dir"
"../test_dir/1_dir/2.txt"
  1. os中提供了函数walk()
def eg2_3():
    """递归返回指定目录下的所有文件/文件夹"""
    for root, dirs, files in os.walk("./test_dir"):
        print("root : {}, dirs : {}, files : {}".format(root, dirs, files))
root : ./test_dir, dirs : ['1_dir'], files : ['1.txt']
root : ./test_dir/1_dir, dirs : ['empty_dir'], files : ['2.txt']
root : ./test_dir/1_dir/empty_dir, dirs : [], files : []

判断路径是否存在

  1. filesystem提供了判断路径是否存在的函数exists()
void eg2_4() {
    /*判断路径是否存在*/
    bool exist = std::filesystem::exists("C:\\Users");
    std::cout << "exist : " << exist << std::endl;
}
exist : 0
  1. os.path中提供了函数exists()
def eg2_4():
    """判断路径是否存在"""
    path = "C:\\Users"
    print("os.path.exists({}) : {}".format(path, os.path.exists(path)))
os.path.exists(C:\Users) : False

判断路径是文件还是目录

  1. filesystem提供了判断路径是文件还是目录的函数。(比较多)
inline _LIBCPP_INLINE_VISIBILITY bool is_block_file(file_status __s) noexcept {
  return __s.type() == file_type::block;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_block_file(const path& __p) {
  return is_block_file(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_block_file(const path& __p,
                                                    error_code& __ec) noexcept {
  return is_block_file(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool
is_character_file(file_status __s) noexcept {
  return __s.type() == file_type::character;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_character_file(const path& __p) {
  return is_character_file(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool
is_character_file(const path& __p, error_code& __ec) noexcept {
  return is_character_file(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_directory(file_status __s) noexcept {
  return __s.type() == file_type::directory;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_directory(const path& __p) {
  return is_directory(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_directory(const path& __p,
                                                   error_code& __ec) noexcept {
  return is_directory(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_empty(const path& __p) {
  return __fs_is_empty(__p);
}

inline _LIBCPP_INLINE_VISIBILITY bool is_empty(const path& __p,
                                               error_code& __ec) {
  return __fs_is_empty(__p, &__ec);
}

inline _LIBCPP_INLINE_VISIBILITY bool is_fifo(file_status __s) noexcept {
  return __s.type() == file_type::fifo;
}
inline _LIBCPP_INLINE_VISIBILITY bool is_fifo(const path& __p) {
  return is_fifo(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_fifo(const path& __p,
                                              error_code& __ec) noexcept {
  return is_fifo(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool
is_regular_file(file_status __s) noexcept {
  return __s.type() == file_type::regular;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_regular_file(const path& __p) {
  return is_regular_file(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool
is_regular_file(const path& __p, error_code& __ec) noexcept {
  return is_regular_file(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_socket(file_status __s) noexcept {
  return __s.type() == file_type::socket;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_socket(const path& __p) {
  return is_socket(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_socket(const path& __p,
                                                error_code& __ec) noexcept {
  return is_socket(__status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_symlink(file_status __s) noexcept {
  return __s.type() == file_type::symlink;
}

inline _LIBCPP_INLINE_VISIBILITY bool is_symlink(const path& __p) {
  return is_symlink(__symlink_status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_symlink(const path& __p,
                                                 error_code& __ec) noexcept {
  return is_symlink(__symlink_status(__p, &__ec));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_other(file_status __s) noexcept {
  return exists(__s) && !is_regular_file(__s) && !is_directory(__s) &&
         !is_symlink(__s);
}

inline _LIBCPP_INLINE_VISIBILITY bool is_other(const path& __p) {
  return is_other(__status(__p));
}

inline _LIBCPP_INLINE_VISIBILITY bool is_other(const path& __p,
                                               error_code& __ec) noexcept {
  return is_other(__status(__p, &__ec));
}
void eg2_5() {
    /*判断路径是文件还是目录*/
    std::filesystem::path file_path = "../test_dir/1.txt";
    std::filesystem::path dir_path = "../test_dir/1_dir";
    std::cout << "is_regular_file(file_path) : "
              << std::filesystem::is_regular_file(file_path)
              << std::endl;
    std::cout << "is_directory(dir_path) : "
              << std::filesystem::is_directory(dir_path)
              << std::endl;
}
is_regular_file(file_path) : 1
is_directory(dir_path) : 1
  1. os.path中提供了函数isfile()isdir()
def eg2_5():
    """判断路径是文件还是目录"""
    file_path = "./test_dir/1.txt"
    dir_path = "./test_dir/1_dir"
    print("os.path.isfile({}) : {}".format(file_path, os.path.isfile(file_path)))
    print("os.path.isdir({}) : {}".format(dir_path, os.path.isdir(dir_path)))
os.path.isfile(./test_dir/1.txt) : True
os.path.isdir(./test_dir/1_dir) : True

返回绝对路径【神奇】

  1. filesystem中提供了函数absolute()
void eg2_6() {
    /*返回绝对路径*/
    std::filesystem::path path = "../test_dir";
    std::filesystem::path abs_path = std::filesystem::absolute(path);
    std::cout << "abs_path : " << abs_path << std::endl;
    std::cout << "exists(abs_path) : " << std::filesystem::exists(abs_path) << std::endl;
}
abs_path : "/Users/xxx/Github/intro_to_C-python/xxx/cmake-build-debug/../test_dir"
exists(abs_path) : 1
  1. 在Windows系统中结果如下。
abs_path : "D:\\GitHub\\intro_to_C-python\\xxx\\test_dir"
exists(abs_path) : 1
  1. os.path中提供了函数abspath()
def eg2_6():
    """返回绝对路径"""
    path = "../../-PyTorch-"
    abs_path = os.path.abspath(path)
    print("abs_path : {}".format(abs_path))
    print("os.path.exists({}) : {}".format(abs_path, os.path.exists(abs_path)))
abs_path : /Users/xxx/Github/-PyTorch-
os.path.exists(/Users/xxx/Github/-PyTorch-) : True

filesystem vs. shutil

shutil = shell + util,对os进行一些补充。

文件拷贝

  1. filesystem中提供了copy_file()函数,可以拷贝文件或空文件夹。
void eg3_1() {
    /*文件拷贝*/
    std::cout << "~~~~~~before copy_file~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir")) {
        std::cout << i.path() << std::endl;
    }
    std::filesystem::copy_file("../test_dir/1.txt", "../test_dir/eg3_1.txt");
    std::cout << "~~~~~~after copy_file~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir")) {
        std::cout << i.path() << std::endl;
    }
}
~~~~~~before copy_file~~~~~~
"../test_dir/1.txt"
"../test_dir/1_dir"
~~~~~~after copy_file~~~~~~
"../test_dir/eg3_1.txt"
"../test_dir/1.txt"
"../test_dir/1_dir"
  1. shutil中提供函数copyfile()
def eg3_1():
    """文件拷贝"""
    print("~~~~~~before copy_file~~~~~~")
    for i in os.listdir("./test_dir"):
        print(i)
    src_path = "./test_dir/1.txt"
    dst_path = "./test_dir/eg3_1.txt"
    shutil.copyfile(src_path, dst_path)
    print("~~~~~~after copy_file~~~~~~")
    for i in os.listdir("./test_dir"):
        print(i)
~~~~~~before copy_file~~~~~~
1.txt
1_dir
~~~~~~after copy_file~~~~~~
eg3_1.txt
1.txt
1_dir

文件删除

  1. filesystem中提供了remove()函数,可以删除文件或空文件夹。
void eg3_2() {
    /*文件删除*/
    std::cout << "~~~~~~before remove~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir")) {
        std::cout << i.path() << std::endl;
    }
    std::filesystem::remove("../test_dir/eg3_1.txt");
    std::cout << "~~~~~~after remove~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir")) {
        std::cout << i.path() << std::endl;
    }
}
~~~~~~before remove~~~~~~
"../test_dir/eg3_1.txt"
"../test_dir/1.txt"
"../test_dir/1_dir"
~~~~~~after remove~~~~~~
"../test_dir/1.txt"
"../test_dir/1_dir"
  1. os中提供了函数remove()
def eg3_2():
    """文件删除"""
    print("~~~~~~before remove~~~~~~")
    for i in os.listdir("./test_dir"):
        print(i)
    rm_path = "./test_dir/eg3_1.txt"
    os.remove(rm_path)
    print("~~~~~~after remove~~~~~~")
    for i in os.listdir("./test_dir"):
        print(i)
~~~~~~before remove~~~~~~
eg3_1.txt
1.txt
1_dir
~~~~~~after remove~~~~~~
1.txt
1_dir

路径拷贝

  1. filesystem中提供了copy()函数,可以按照选项(是否递归)拷贝文件或文件夹。
void eg3_3() {
    /*路径拷贝*/
    std::cout << "~~~~~~before copy[1_dir]~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir/1_dir")) {
        std::cout << i.path() << std::endl;
    }
    std::filesystem::copy("../test_dir/1_dir",
                          "../test_dir/eg3_3_dir",
                          std::filesystem::copy_options::recursive);
    std::cout << "~~~~~~after copy[eg3_3_dir]~~~~~~" << std::endl;
    for (const auto &i :std::filesystem::directory_iterator("../test_dir/eg3_3_dir")) {
        std::cout << i.path() << std::endl;
    }
}
~~~~~~before copy[1_dir]~~~~~~
"../test_dir/1_dir/empty_dir"
"../test_dir/1_dir/2.txt"
~~~~~~after copy[eg3_3_dir]~~~~~~
"../test_dir/eg3_3_dir/empty_dir"
"../test_dir/eg3_3_dir/2.txt"
  1. shutil提供了函数copytree()
def eg3_3():
    """路径拷贝"""
    print("~~~~~~before copy[1_dir]~~~~~~")
    for i in os.listdir("./test_dir/1_dir"):
        print(i)
    src_path = "./test_dir/1_dir"
    dst_path = "./test_dir/eg3_3_dir"
    shutil.copytree(src_path, dst_path)
    print("~~~~~~after copy[eg3_3_dir]~~~~~~")
    for i in os.listdir("./test_dir/eg3_3_dir"):
        print(i)
~~~~~~before copy[1_dir]~~~~~~
empty_dir
2.txt
~~~~~~after copy[eg3_3_dir]~~~~~~
empty_dir
2.txt

递归删除

  1. filesystem中提供了remove_all()函数,可以递归删除文件或文件夹。
void eg3_4() {
    /*递归删除*/
    std::filesystem::path dir_path = "../test_dir/eg3_3_dir";
    std::cout << "exists(dir_path) : " << std::filesystem::exists(dir_path) << std::endl;
    std::filesystem::remove_all(dir_path);
    std::cout << "exists(dir_path) : " << std::filesystem::exists(dir_path) << std::endl;
}
exists(dir_path) : 1
exists(dir_path) : 0
  1. shutil提供了函数rmtree()
def eg3_4():
    """递归删除"""
    dir_path = "./test_dir/eg3_3_dir"
    print("os.path.exists({}) : {}".format(dir_path, os.path.exists(dir_path)))
    shutil.rmtree(dir_path)
    print("os.path.exists({}) : {}".format(dir_path, os.path.exists(dir_path)))
os.path.exists(./test_dir/eg3_3_dir) : True
os.path.exists(./test_dir/eg3_3_dir) : False