目录
3.recursive_directory_iterator
filesystem库是一个可移植的文件系统操作库,已被收入 C ++17 标准。它在底层做了大量的工作,使用POSⅨ标准表示文件系统的路径,其接口类似标准库的容器和迭代器,使 C++ 具有了类似脚本语言的功能,可以跨平台操作目录、文件,写出通用的脚本程序。
由于文件系统位于程序之外,是不可控且全局共享的,所以访问目录或文件随时都有可能发生异常,如文件已经删除、文件已经存在等。为了程序的“健壮性”,应总使用 try–catch 块来保护文件访问代码。
一、path
filesystem库的核心类是 path ,它屏蔽了不同文件系统的差异,使用可移植的 POSⅨ语法提供了通用的目录、路径表示,并且支持 POSⅨ的符号链接概念。
path 仅仅用于表示路径,它而并不关心路径中的文件或目录是否存在,路径也可能是个在当前文件系统中无效的名字,如在 Windows操作系统下不允许文件名或目录名使用冒号、尖括号、星号、问号等,但 path 并不禁止这种表示(path路径中可以存在?*:等)。
1.path构造
path的构造函数没有被声明为 explicit ,因此字符串可以被隐式地转换为 path 对象,这在编写操作文件系统的代码时非常方便,不用再创建一个临时的 path 对象。
path的构造函数可以是空构造函数,接收空路径对象,不表示任何路径;也可以是 C 字符串和 string ;也可以是一个指定首末迭代器字符串的序列区间。路径的分隔符由类内部的静态常量preferred_separator 定义, UNIX是斜杠“ / ”,Windows是反斜杠“ \ ”。
path使用标准的 POSX 语法提供可移植的路径表示,使用斜杠“ / ”来分隔文件名和目录名,点号“.”表示当前目录,双点号“..”表示上一级目录。
path也支持操作系统的原生路径表示,在Windows下使用盘符,分隔符使用反斜杠“ \ ”。(因为反斜杠被C++定义为转义符,在字符串中使用反斜杠表示路径的时候必须联系写\\才能被识别为一个\)
#include <boost/filesystem.hpp>
boost::filesystem::path path0; //空路径对象
boost::filesystem::path path1("./test.txt"); //.表示当前目录
boost::filesystem::path path2("../BoostStudyFromNinthChapter/test.txt");//..表示上一级目录
boost::filesystem::path path3("D:\\FCJProject\\VSProgram\\BoostStudyFromNinthChapter\\BoostStudyFromNinthChapter");
//判断路径是否为空
if (path0.empty())
{
std::cout << "path0 is empty" << std::endl;
}
2.追加路径
path重载了 operator/= ,可以像使用普通路径一样用“ / ”来追加路径,成员函数 append( )也可以追加一个字符串序列。
operator+= 和 concat( )的作用与 operator/=类似,但它仅连接字符串,不会添加路径分隔符。
//追加路径 四种方法(注意后两者需要加路径分隔符)
path3.append("test.txt");
path3 /= "test.txt";
path3.concat("\\test.txt");
path3 += "\\test.txt";
3.获取完整绝对路径
自由函数system_complete( )可以返回路径在当前文件系统中的完整路径(也就是通常所说的绝对路径)。
std::cout << "path1的完整绝对路径:" << boost::filesystem::system_complete(path1) << std::endl;//path3的完整绝对路径:"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter\test.txt"
std::cout << "path2的完整绝对路径:" << boost::filesystem::system_complete(path2) << std::endl;//同上
std::cout << "path3的完整绝对路径:" << boost::filesystem::system_complete(path3) << std::endl;//同上
4.文件名可移植性判断
为了提高程序在不同文件系统上的可移植性,filesystem库提供了一系列的文件名(或目录名)检查函数,可以根据系统的命名规则判断一个文件名字符串的有效性,从而尽可能地为程序员编写可移植的程序创造便利条件。
自由函数 portable_posix_name( )和windows_name( )分别检测文件名字符串是否符合 POSIX 规范和 Windows 规范,保证名字可以移植到符合 POSIX规范的 UNIX操作系统和Windows操作系统上。(POSIX规范只有一个很小的字符集用于文件名,包括大小写字母、点号、下画线和连字符,而Windows的范围要广一些,它只不允许“ <>?∶ |/\ ”等少量字符。)
自由函数portable_name ( ) 判断名字是否是一个可移植的文件名,相当于portable_posix_name( )&&windows_name( ),但名字不能以点号或连字符开头 , 并允许表示当前目录的 "." 和父目录的 ".."。它可以保证文件名可以移植到所有现代操作系统和一些旧的操作系统上。
自由函数portable_directory_name( ) 的判断规则更严格 , 它包含了portable_name( ) , 并且要求名字中不能出现点号 , 其目录名可以移植到OpenVMS操作系统上。
自由函数portable_file_name( ) 类似portable_directory_name ( ),它提供可移植性更高的文件名,它要求文件名中最多有一个点号,且后缀名不能超过 3 个字符。
自由函数 native( )判断文件名是否符合本地文件系统的命名规则,在 Windows 操作系统下它等同于windows_name ( ) ,而在其他操作系统下则只是简单地判断文件名不是空格且不包含斜杠。
//文件名是否符合POSIX规范
std::cout << "&_www.txt是否符合POSIX规范?" << boost::filesystem::portable_posix_name("&_www.txt") << std::endl;//0
std::cout << "._www.txt是否符合POSIX规范?" << boost::filesystem::portable_posix_name("._www.txt") << std::endl;//1
//文件名是否符合Windows规范
std::cout << "&_www.txt是否符合Windows规范?" << boost::filesystem::windows_name("&_www.txt") << std::endl;//1
std::cout << "._www.txt是否符合Windows规范?" << boost::filesystem::windows_name("._www.txt") << std::endl;//1
//文件名是否可移植 portable_name即portable_posix_name与windows_name为true,并且不以.-开头
std::cout << "&_www.txt是否可移植?" << boost::filesystem::portable_name("&_www.txt") << std::endl;//0
std::cout << "._www.txt是否可移植?" << boost::filesystem::portable_name("._www.txt") << std::endl;//0
//portable_directory_name
std::cout << "._www.txt是否可移植?" << boost::filesystem::portable_directory_name("_www.txt") << std::endl;//0
//portable_file_name
std::cout << "._www.txt是否可移植?" << boost::filesystem::portable_file_name("_www.txt") << std::endl;//1
//native()
std::cout << "._www.txt是否符合本地文件系统命名规则?" << boost::filesystem::native("_www.txt") << std::endl;//1
5.路径分析
成员函数 string( )以字符串的形式返回标准格式的路径表示;
成员函数 parent_path( )返回路径中的父路径;
成员函数 filename( )返回路径中的全文件名;
成员函数 stem( )返回路径中的不含扩展名的文件名;
成员函数 extension( )返回路径中的扩展名。
成员函数 is_absolute ( ) 用于检测 path 是否是一个绝对 ( 完整 ) 路径 , 这需要依据具体的文件系统的表示。
成员函数 root_name ( ) 、 root_directory( )和root_path ( ) 用于处理根目录,如果 path 中含有根,那么它们分别返回根的名字、根目录和根路径,它们的返回结果也是path对象 。
成员函数 relative_path( )返回 path 的相对路径,相当于去掉了 root_path( )。
成员函数 has_xxx( )可以判断是否存在对应的路径。
std::cout << "path3标准路径:" << path3.string() << std::endl;//path3标准路径:D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter\test.txt
std::cout << "path3父路径:" << path3.parent_path() << std::endl;//path3父路径:"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter"
std::cout << "path3全文件名:" << path3.filename() << std::endl;//path3全文件名:"test.txt"
std::cout << "path3不含扩展名的文件名:" << path3.stem() << std::endl;//path3不含扩展名的文件名:"test"
std::cout << "path3扩展名:" << path3.extension() << std::endl;//path3扩展名:".txt"
std::cout << "path3是绝对完整路径吗:" << path3.is_absolute() << std::endl;//path3是绝对完整路径吗:1
std::cout << "path2是绝对完整路径吗:" << path2.is_absolute() << std::endl;//path2是绝对完整路径吗:0
std::cout << "path3 根目录名字:" << path3.root_name() << std::endl;//path3 根目录名字:"D:"
std::cout << "path3 根路径:" << path3.root_path() << std::endl;//path3 根路径:"D:\"
std::cout << "path3 根目录:" << path3.root_directory() << std::endl;//path3 根目录:"\"
std::cout << "path2 根目录名字:" << path2.root_name() << std::endl;//path2 根目录名字:""
std::cout << "path2 根路径:" << path2.root_path() << std::endl;//path2 根路径:""
std::cout << "path2 根目录:" << path2.root_directory() << std::endl;//path2 根目录:""
std::cout << "path3 相对路径:" << path3.relative_path() << std::endl;//path3 相对路径:"FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter\test.txt"
if (path2.has_relative_path())
{
std::cout << "path2 相对路径:" << path2.relative_path() << std::endl;//path2 相对路径:"../BoostStudyFromNinthChapter/test.txt"
}
//比较
std::cout << "path2和path3的文件名相同吗?" << (path2.filename() == path3.filename()) << std::endl;//path2和path3的文件名相同吗?1
path还提供常量迭代器 begin( )和 end( ),可以迭代路径中的字符串,解引用迭代器返回的是const path& 。
for (auto item = path3.begin(); item != path3.end(); item++)
{
std::cout << *item << " ";//"D:" "/" "FCJProject" "VSProgram" "BoostStudyFromNinthChapter" "BoostStudyFromNinthChapter"
}
6.路径修改
remove_filename( )函数可以删除路径中最后的文件名,把 path 变为纯路径表示。
replace_extension( )可以变更文件的扩展名。
path3.remove_filename();
std::cout << "path3去掉文件名:" << path3 << std::endl;//path3去掉文件名:"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter"
path2.replace_extension("pdb");//注意:是处理路径,真正的文件后缀并没有更改
std::cout << "path2修改后缀名:" << path2 << std::endl;//path2修改后缀名:"../BoostStudyFromNinthChapter/test.pdb"
7.异常处理
filesystem库使用异常 filesystem_error来处理文件操作时发生的错误 , 它是system库中system_error的子类。
try {
boost::filesystem::file_size(path2);
}
catch (boost::filesystem::filesystem_error e)
{
std::cout << e.what()<< std::endl;//boost::filesystem::file_size: 系统找不到指定的文件。: "../BoostStudyFromNinthChapter/test.pdb"
}
二、文件
filesystem库提供一个文件状态类file_status及一组文件属性相关函数。
1.文件状态类file_status
file_status成员函数 type( )用于检查文件的属性(如是否存在、是否是目录、是否是符号链接等)。
file_status成员函数permissions( )用于获得文件的访问权限(用户的读、写权限,以及用户组的读、写权限等)。
通常我们不会直接使用file_status类,而是用相关函数操作file_status对象。
自由函数status( )和symlink _ status( )测试路径的状态,如果路径不能被解析,那么会抛出异常filesystem_error 。
自由函数status_known ( )检查文件状态 s ,返回s.type != status_error 。
if (boost::filesystem::status(path1).type() == boost::filesystem::regular_file)
{
std::cout << "路径1的文件是常规文件" << std::endl;
}
//文件权限 (有问题)
//if (boost::filesystem::status(path1).permissions() == boost::filesystem::owner_read)
//{
// std::cout << "路径1的文件是只读文件" << std::endl;
//}
//boost::filesystem::perms perms = boost::filesystem::status(boost::filesystem::system_complete(path1)).permissions();
//std::cout << perms << std::endl;
2.文件属性
受可移植的限制,很多文件属性不是各平台共通的,因此 filesystem 库仅提供了少量的文件属性操作。
自由函数 initial_path( )返回程序启动时(进入main函数)的路径。
自由函数 current_path( )返回当前路径。它和initial_path( )返回的都是一个完整路径(绝对路径)。
自由函数 file_size( )以字节为单位返回文件的大小,注意:路径文件必须是个普通文件(is_regular_file)。
自由函数 last_write_time( )返回文件的最后修改时间,是一个 std::time_t 。last_write_time( )还可以额外接收一个 time_t参数,修改文件的最后修改时间,其效果如同使用 UNIX 的 touch 命令。
std::cout << "程序启动时的路径:" << boost::filesystem::initial_path() << std::endl;//程序启动时的路径:"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter"
std::cout << "当前路径:" << boost::filesystem::current_path() << std::endl;//当前路径:"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter"
if (boost::filesystem::is_regular_file(path1))
{
std::cout << "path1文件大小:" << boost::filesystem::file_size(path1) << "字节" << std::endl;//path1文件大小:11字节
}
std::time_t time = boost::filesystem::last_write_time(path1);
std::cout << "path1文件最后修改时间:" << std::ctime(&time) << std::endl;//path1文件最后修改时间:Mon Nov 29 17:55:51 2021
//修改文件最后修改时间
boost::filesystem::last_write_time(path1, std::time(0));
自由函数space( )可以返回一个space_info结构,它表明了该路径下的磁盘空间的分配情况。
boost::filesystem::space_info space_info = boost::filesystem::space(path1);
std::cout << "available:" << space_info.available / 1024 / 1024 / 1024 <<
"G,capacity:" << space_info.capacity / 1024 / 1024 / 1024 <<
"G,free:" << space_info.free / 1024 / 1024 / 1024 << "G" << std::endl;//available:271G,capacity:375G,free:271G
3.文件操作
filesystem库基于path的路径表示提供了基本的文件操作函数:创建目录(create_directory)、文件改名( rename )、文件删除( remove )﹑文件拷贝( copy_file ) 、创建符号链接
( create_symlink ) 等。
boost::filesystem::path filePath("./test");//test文件夹
if (boost::filesystem::exists(filePath))
{
if (boost::filesystem::is_empty(filePath))
{
//文件删除
bool ret = boost::filesystem::remove(filePath);
std::cout << "文件夹是否删除成功?" << ret << std::endl;
}
}
//创建文件夹
bool ret = boost::filesystem::create_directory("./dic");
if (ret)
{
//拷贝文件
ret =boost::filesystem::copy_file("./test.txt", "./dic/test_copy.txt");
std::cout << "文件夹是否拷贝成功?" << ret << std::endl;
}
//重命名
boost::filesystem::rename("./dic/test_copy.txt", "./dic/test_copy2.txt");
boost::filesystem::rename(path3 / "test.txt", "./test_copy2.txt");
//创建多层文件夹
ret = boost::filesystem::create_directories(path3/"dic1"/"dic2");
//创建符号链接(不会用)
try {
boost::filesystem::create_symlink("./1.lnk", "./test.lnk");
}
catch (boost::filesystem::filesystem_error e)
{
std::cout << e.what() << std::endl;//boost::filesystem::create_symlink: 客户端没有所需的特权。: "./1.lnk", "./test.lnk"
}
三、目录迭代
filesystem库使用 directory_iterator 提供了迭代一个目录下的所有文件的功能。空的构造函数生成一个逾尾 end 迭代器,传入path对象构造将开始一个迭代操作,反复调用operator ++ 即可遍历目录下的所有文件。
需要注意的是directory_iterator迭代器返回的对象并不是 path ,而是一个 directory_entry 对象,但由于 directory_entry 类定义了一个到 path 的类型转换函数,因此可以在需要 path 类型的语境中将其隐式转换成 path 类型。directory_entry 可以用 path( )方法返回路径,status( )返回路径的状态,它还重载了各种比较操作符,可以执行比较操作。
directory_iterator 只能迭代本层目录,不支持深度遍历目录。
1.单层遍历
void StudySystem::TestFileSystem1()
{
boost::filesystem::path path("D:\\FCJProject\\VSProgram\\BoostStudyFromNinthChapter");
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator pos(path); pos != end; pos++)
{
std::cout << *pos <<",即:"<<pos->path() << std::endl;
}
//"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\.vs"
//"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter"
//"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\BoostStudyFromNinthChapter.sln"
//"D:\FCJProject\VSProgram\BoostStudyFromNinthChapter\Debug
}
2.递归实现深度遍历
void StudySystem::TestFileSystem2(const boost::filesystem::path& path)
{
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator pos(path); pos != end; pos++)
{
std::cout << *pos << std::endl;
if (boost::filesystem::is_directory(pos->path()))
{
TestFileSystem2(pos->path());
}
}
}
3.recursive_directory_iterator
filesystem库的recursive_directory_iterator类可以递归遍历目录,具有目录迭代器的基本功能,operator ++ 会令它返回目录中的下一个文件。它的特别之处是可以深度搜索目录,迭代当前目录及子目录下的所有文件。
成员函数 depth ( ) 返回当前的目录深度m_level , 当recursive_directory_iterator构造时 ( 未开始遍历 )m_level == 0 , 每深入一层子目录则 m_ level 增加 , 退出时则 m_level 减少。
成员函数 pop ( ) 用于退出当前目录层次的遍历 , 同时 m_level--。当迭代到—个目录时,disable_recursion_pending ( ) 可以让目录不参与遍历 , 其行为等价于 directory_iterator。
void StudySystem::TestFileSystem3()
{
boost::filesystem::path path("D:\\FCJProject\\VSProgram\\BoostStudyFromNinthChapter");
boost::filesystem::recursive_directory_iterator end;
for (boost::filesystem::recursive_directory_iterator pos(path); pos != end; pos++)
{
std::cout <<*pos<<":"<< pos.depth() << std::endl;
}
}
四、文件流操作
filesystem库提供了大量的操作文件系统的方法,可以很方便地操作文件或目录,但它使用的
是 path 对象。C++17里的文件流增加了对path对象的支持,和boost::filesystem::ifstream可以无缝对接。
void StudySystem::ReadFile()
{
boost::filesystem::path path("D:\\FCJProject\\VSProgram\\BoostStudyFromNinthChapter\\BoostStudyFromNinthChapter\\test.txt");
boost::filesystem::ifstream _ifstream(path);
if (_ifstream.is_open())
{
std::cout << _ifstream.rdbuf() << std::endl;
_ifstream.close();
}
}