文章目录
-
- 1.简介
- 2.路径合法性
- 3.路径处理及迭代器
- 4.设置权限
- 5.文件操作
- 6.迭代目录
-
- [6.1. directory_iterator](#6.1. directory_iterator)
- 6.2.recursive_directory_iterator
-
- [6.2.1. 类摘要](#6.2.1. 类摘要)
- [6.2.2 示例代码](#6.2.2 示例代码)
- 7.查找文件、模糊查找文件、复制文件
- 8.总结
1.简介
boost::filesystem是Boost C++ Libraries中的一个模块,主要作用是处理文件(Files)和目录(Directories)。该模块提供的类boost::filesystem::path专门用来处理路径。而且,该模块中还有很多独立的函数能够用来执行创建目录、检查文件是否存在等任务。
涉及头文件
cpp
// filesystem
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
2.路径合法性
检查路径字符串的合法性。
cpp
// path 仅仅是用于表示路径,而并不关心路径中的文件或目录是否存在,路径也可能是个在当前文件系统中无效的名字,例如在Windows 下不允许文件名或目录名使用冒号、尖括号、星号、问号,等等,但path 并不禁止这种表示
path p("/::/*/?/<>");
// 完全合法
// -----------------------------------------------------------------------------------------
// 为了提高程序在不同文件系统上的可移植性,filesystem库提供了一系列的文件名(或目录名)检查函数,可以根据系统的命名规则判断一个文件名字符串的有效性,从而尽可能地为程序员编写可移植的程序创造便利。
// portable_posix_name() 检测文件名字符串是否符合POSIX规范(包括大小写字母、点号、下画线和连字符)
// windows_name() 检测文件名字符串是否符合Windows规范(范围要广一些,仅不允许<>?:|/\等少量字符)
// portable_name() 相当于 portable_posix_name()&&windows_name()
// 但名字不能以点号或者连字符开头,并允许表示当前目录的"."和父目录的".."
// portable_directory_name 包含了portable_name(),名字中不能出现点号,目录名可以移植到OpenVMS操作系统上
// portable_file_name 类似portable_directory_name()
// 提供更可移植的文件名,它要求文件名中最多有一个点号,且后缀名不能超过3个字符.
cout << "protable_posix_name, posix:" << portable_posix_name("w+abc.xxx")
<< "; windows" << windows_name("w+abc.xxx") << endl;
cout << "portable_name:" << portable_name("w+()abc.txt") << "; "
<< portable_name("./abc") << endl;
cout << "portable_directory_name:" << portable_directory_name("a.txt") << "; "
<< portable_directory_name("abc") << endl;
cout << "portable_file_name:" << portable_directory_name("a.bc") << "; "
<< portable_file_name("y.conf") << endl;
/*
protable_posix_name, posix:0; windows1
portable_name:0; 0
portable_directory_name:0; 1
portable_file_name:0; 0
*/
3.路径处理及迭代器
cpp
boost::filesystem::path p("/usr/local/include/xxx.hpp");
// 以字符串的形式返回标准格式的路径
cout << p.string() << endl; // /usr/local/include/xxx.hpp
// 父路径、全文件名、不含扩展名的文件名和扩展名
cout << p.parent_path() << endl; // /usr/local/include/ (返回的是path类型)
cout << p.stem() << endl; // xxx
cout << p.filename() << endl; // xxx.hpp
cout << p.extension() << endl; // .hpp
// 判断是否是绝对路径
assert(p.is_absolute()); // Windows 断言不成立
assert(boost::filesystem::system_complete(p).is_absolute()); // 总是完整路径
// 根的名字、根目录和根路径 若Windows下 path p("c:/xxx/yyy")
cout << p.root_name() << endl; // linux 空 windows c:
cout << p.root_directory() << endl; // linux / windows /
cout << p.root_path() << endl; // linux / windows c:/
// 判断路径是否有文件名或者父路径
assert(!p.has_root_name());
assert( p.has_root_path());
assert( p.has_parent_path());
// 使用成员函数compare()基于字典序并且大小写敏感比较路径字符串
// 提供operator==、operator!=、operator<等操作符
boost::filesystem::path p1("/test/1.cpp");
boost::filesystem::path p2("/TEST/1.cpp");
boost::filesystem::path p3("/abc/1.cpp");
assert(p1 != p2);
assert(p2 < p3);
// 迭代器
path p = "/boost/tools/libs";
BOOST_FOREACH(auto& x, p) { // 使用foreach 算法,也可以用for
cout << "["<< x << "]"; // 输出路径字符串
}
/*
[/][boost][tools][libs]
*/
4.设置权限
只读文件,需要去除只读属性,才能进行编辑或者删除。
cpp
boost::filesystem::permissions(xmlFilename,boost::filesystem::add_perms | boost::filesystem::owner_write | boost::filesystem::group_write | boost::filesystem::others_write);//设置属性非只读
boost::filesystem::remove(xmlFilename);//删除文件
5.文件操作
filesystem库基于path的路径表示提供了基本的文件操作函数,如创建目录(create_directory)、文件改名(rename)、文件删除(remove)、文件拷贝(copy_file)、创建符号链接(create_symlink),等等,这些函数的名字都很容易理解。
cpp
namespace fs = boost::filesystem; // 名字空间别名
boost::filesystem::path ptest = "./test";
if (exists(ptest)) { // 检查路径是否存在
if (boost::filesystem::is_empty(ptest)) { // 注意名字空间限定
boost::filesystem::remove(ptest); // remove只能删除空目录或文件
}
else {
boost::filesystem::remove_all(ptest); // remove_all 可以递归删除
}
}
assert(!boost::filesystem::exists(ptest)); // 该目录已经被删除
boost::filesystem::create_directory(ptest); // 创建一个目录
boost::filesystem::copy_file("/usr/local/include/boost/version.hpp", ptest / "a.txt");
assert(boost::filesystem::exists(ptest / "a.txt"));
boost::filesystem::copy_directory("E:\\VSCode","D:\\VSCode"); //复制目录,可以使用copy_directory(),使用时需要注意如果目录已经存在,该函数会失败;
boost::filesystem::rename(ptest / "a.txt", ptest / "b.txt"); // 改名
assert(boost::filesystem::exists(ptest / "b.txt"));
// 使用create_directories 可以一次创建多级目录
boost::filesystem::create_directories(ptest / "sub_dir1" / "sub_dir2");
// 注意
// cpp标准的type_traits库中有一个同名的元函数is_empty,所以我们在这段代码中为is_empty()函数加上了名字空间限定,避免名字冲突。
6.迭代目录
filesystem库提供了迭代一个目录下的所有文件的功能的类:
directory_iterator,基于boost.iterator库的iterator_facade。
recursive_directory_iterator,递归遍历文件系统目录。
6.1. directory_iterator
6.1.1.类摘要
cpp
// directory_iterator只能迭代本层目录,不支持深度遍历目录
class directory_iterator : public boost::iterator_facade<
directory_iterator,
directory_entry, // 解引用返回类型
boost::single_pass_traversal_tag >
{
public:
directory_iterator(){}
directory_iterator(const directory_iterator&);
explicit directory_iterator(const path& p);
~directory_iterator();
directory_iterator& operator=(const directory_iterator&);
directory_iterator& operator++();
};
// 注意:directory_iterator迭代器返回的对象并不是path,而是一个directory_entry对象
class directory_entry
{
public:
const path& path() const; // 返回path 对象
file_status status() const; // 获取文件状态
file_status symlink_status() const;
};
6.1.2.单层目录迭代
directory_iterator只能迭代本层目录,不支持深度遍历目录。
cpp
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
void test() {
// directory_iterator,空的构造函数生成一个逾尾end迭代器,传入path对象构造将开始一个迭代操作,
// 反复调用operator++即可遍历目录下的所有文件。
namespace fs = boost::filesystem;
fs::directory_iterator end;
for (fs::directory_iterator pos("C:/Users/ajz/Desktop/说明书模板"); pos != end; ++pos) {
std::cout << *pos << std::endl;
}
// 定义一个std::pair区间,使用foreach算法简化迭代
// 定义一个迭代器的区间
typedef std::pair<fs::directory_iterator, fs::directory_iterator> dir_range;
dir_range dr(fs::directory_iterator("C:/Users/ajz/Desktop/说明书模板"), // 迭代起点
fs::directory_iterator()); // 迭代终点
BOOST_FOREACH(auto & x, dr) { // 迭代区间
std::cout << x << std::endl;
}
}
6.1.3.directory_iterator深度遍历目录(递归)
cpp
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
void recursive_dir(const boost::filesystem::path& dir) // 递归遍历目录
{
boost::filesystem::directory_iterator end; // 结束迭代器调用
for (boost::filesystem::directory_iterator pos(dir); pos != end; ++pos) {
if (is_directory(*pos)) {
recursive_dir(*pos); // 是目录则递归遍历
}
else {
std::cout << *pos << std::endl; // 不是目录则输出路径
}
}
}
//调用
recursive_dir(boost::filesystem::path("C:/Users/ajz/Desktop/说明书模板"));
6.2.recursive_directory_iterator
6.2.1. 类摘要
cpp
class recursive_directory_iterator // 省略构造、析构函数以及迭代器通用操作
{
public:
int level() const; // 目录深度
void pop(); // 退出当前目录的遍历
void no_push(); // 不遍历本目录
private:
int m_level; // 目录深度成员变量
};
/*
成员函数level()返回当前的目录深度m_level,当rd_iterator构造时(未开始遍历)m_level==0,每深入一层子目录则m_level增加,退出时减少。
成员函数pop()用于退出当前目录层次的遍历,同时--m_level。
当迭代到一个目录时,no_push()可以让目录不参与遍历,使rd_iterator的行为等价于directory_iterator。
*/
6.2.2 示例代码
cpp
void test() {
//深度迭代代码,展示文件夹和文件
rd_iterator end;
for (rd_iterator pos("C:/Users/ajz/Desktop/说明书模板"); pos != end; ++pos) {
std::cout << "level" << pos.level() << ":" << *pos << std::endl;
}
// 下面的代码使用no_push()令rd_iterator的行为等价于directory_iterator:
//rd_iterator end;
for (rd_iterator pos("C:/Users/ajz/Desktop/说明书模板"); pos != end; ++pos) {
if (is_directory(*pos)) {
pos.no_push(); // 使用no_push(),不深度遍历
}
std::cout << *pos << std::endl;
}
}
7.查找文件、模糊查找文件、复制文件
optional库使用"容器"语义,包装了"可能产生无效值"的对象,实现了"未初始化"的概念。
7.1.查找文件
cpp
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <list>
typedef boost::filesystem::recursive_directory_iterator rd_iterator;
boost::optional<std::list<boost::filesystem::path>> FindFile(boost::filesystem::path dir, std::string filename)
{
// 返回值类型定义
typedef boost::optional<std::list<boost::filesystem::path>> result_type;
// 检查目录的有效性
if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) {
return result_type();
}
// 递归迭代器
rd_iterator end;
std::list<boost::filesystem::path> lstPath = std::list<boost::filesystem::path>();
for (rd_iterator pos(dir); pos != end; ++pos) {
// 不是目录、文件名相等
if ((!is_directory(*pos)) && (pos->path().filename() == filename)) {
lstPath.push_back(pos->path());
}
}
return result_type(lstPath);
}
int main(int argc, char* argv[])
{
boost::optional<std::list<boost::filesystem::path>> r=FindFile(boost::filesystem::path("C:/Users/ajz/Desktop/说明书模板"),"说明书变量部分.png");
std::list<boost::filesystem::path> lstPath = r.value();
// 不存在提示并return
if (lstPath.size() == 0) {
std::cout << "file not found." << std::endl;
return -1;
}
for (boost::filesystem::path p : lstPath) {
std::cout << p << std::endl;
}
return 0;
}
7.2.正则表达式模糊文件查找
cpp
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/xpressive/xpressive_dynamic.hpp>
#include <iostream>
#include <list>
typedef boost::filesystem::recursive_directory_iterator rd_iterator;
std::vector<boost::filesystem::path> FindFiles(const boost::filesystem::path& dir, const std::string& filename)
{
boost::xpressive::sregex_compiler rc; // 正则表达式工厂
if (!rc[filename].regex_id()) {
std::string str = boost::replace_all_copy(
boost::replace_all_copy(filename, ".", "\\."),
"*", ".*"); // 处理文件名
rc[filename] = rc.compile(str); // 创建正则表达式
}
typedef std::vector<boost::filesystem::path> result_type; // 返回值类型定义
result_type v;
if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) // 目录检查
{
return v;
}
rd_iterator end; // 递归迭代器逾尾位置
for (rd_iterator pos(dir); pos != end; ++pos) {
if (!is_directory(*pos) && boost::xpressive::regex_match(pos->path().filename().string(), rc[filename])) {
v.push_back(pos->path()); // 找到,加入vector
}
}
return v; // 返回查找的结果
}
int main()
{
auto v = FindFiles("C:/Users/ajz/Desktop/说明书模板", "*.txt");
std::cout << v.size() << std::endl;
for (boost::filesystem::path& p : v) {
std::cout << p << std::endl;
}
return 0;
}
7.3.拷贝目录
cpp
#include <iostream>
#define BOOST_ALLOW_DEPRECATED_HEADERS
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <boost/progress.hpp>
#include <boost/xpressive/xpressive_dynamic.hpp>
#include <iostream>
#include <list>
typedef boost::filesystem::recursive_directory_iterator rd_iterator;
std::vector<boost::filesystem::path> FindFiles(const boost::filesystem::path& dir, const std::string& filename)
{
boost::xpressive::sregex_compiler rc; // 正则表达式工厂
if (!rc[filename].regex_id()) {
std::string str = boost::replace_all_copy(
boost::replace_all_copy(filename, ".", "\\."),
"*", ".*"); // 处理文件名
rc[filename] = rc.compile(str); // 创建正则表达式
}
typedef std::vector<boost::filesystem::path> result_type; // 返回值类型定义
result_type v;
if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) // 目录检查
{
return v;
}
rd_iterator end; // 递归迭代器逾尾位置
for (rd_iterator pos(dir); pos != end; ++pos) {
if (!is_directory(*pos) && boost::xpressive::regex_match(pos->path().filename().string(), rc[filename])) {
v.push_back(pos->path()); // 找到,加入vector
}
}
return v; // 返回查找的结果
}
size_t CopyFiles(const boost::filesystem::path& from_dir, const boost::filesystem::path& to_dir, const std::string& filename = "*")
{
if (!boost::filesystem::is_directory(from_dir)) { // 源必须是个目录
std::cout << "args is not a dir." << std::endl;
return 0;
}
std::cout << "prepare for copy, please wait..." << std::endl;
auto v = FindFiles(from_dir, filename); // 查找源的所有文件
if (v.empty()) { // 空目录则不拷贝
std::cout << "0 file copied." << std::endl;
return 0;
}
std::cout << "now begin copy files ..." << std::endl;
boost::filesystem::path tmp;
boost::progress_display pd(v.size()); // 进度显示
for (auto& p : v) { // 变量容器
// 拆分基本路径与目标路径
tmp = to_dir / p.string().substr(from_dir.string().length());
if (!boost::filesystem::exists(tmp.parent_path())) { // 创建子目录
boost::filesystem::create_directories(tmp.parent_path());
}
boost::filesystem::copy_file(p, tmp); // 拷贝文件
++pd; // 更新进度
}
std::cout << v.size() << " file copied." << std::endl;
return v.size(); // 完成拷贝
}
int main()
{
CopyFiles("C:/Users/ajz/Desktop/说明书模板", "C:/Users/ajz/Desktop/说明书模板1");
return 0;
}
8.总结
filesystem模块提供了关于文件夹和文件常规操作的绝大部分内容,能够跨平台,接口简单易用。