一、文件处理其实开发者在实际的开发中都会发现对文件IO的操作是相当麻烦的。如果只是在单一的平台上开发可能还稍微好一些只需处理本平台相关的操作接口就可以了。但是又发现几乎所有的对文件或文件夹的操作都存在着一个共性那就是相关的接口以及数据结构都相当的复杂。大家当然可以理解为了兼容内核的底层库必须要把相关的细节暴露出来的设计理念。但对于更多的应用者来说其实有很多的细节意义并不大。比如在前面Linux的数据结构“struct statvfs”可能很多开发者见而生畏。二、std::filesystem随着技术的发展跨平台的需求越来越多而C的标准也在不断在迭代。在C17的标准中提供了一个filesystem库它解决了原有接口不统一、跨平台不方便不兼容以及数据结构复杂的痛点。特别是通过分解相关功能利用不同的模块来处理文件相关的各类属性让上层应用者不必关心过多的文件操作细节。同时它还支持Unicode、软硬链接及权限管理等操作为关心细节操作的开发者提供了更方便的接口操作方法。有过Boost开发经验的很可能就会想到Boost中的Filesystem没错。不然怎么会把Boost称为标准库的预备库呢filesystem的主要相关内容如下可以发现其包含的相关的内容还是相当多的。不过对于普通开发者最方便的就是解决了文件路径操作的不方便不用自己再手动的来回来去的组装和拆解文件路径了。三、应用下面对filesystem的主要用法进行分析和说明文件路径操作这可以说是开发者的福音其引入的filesystem::path提供了路径字符串的拆分、组合及相关的解析等。最典型的就是直接从全路径中获取文件名等#includefilesystem#includeiostreamnamespace fsstd::filesystem;intmain(){std::coutfs::path(/foo/bar.txt).filename()\n;std::coutfs::path(/foo).replace_filename(bar)\n;fs::path p;std::coutstd::boolalpha(pfoo/bar).remove_filename()\tp.has_filename()\n;std::coutfs::path(/foo/bar.txt).extension()\n;for(constfs::path p:{/foo/bar.txt,/foo/.bar,foo.bar.baz.tar})std::coutpath: p, stem: p.stem()\n;std::cout\n;for(fs::path pfoo.bar.baz.tar;!p.extension().empty();pp.stem())std::coutpath: p, extension: p.extension()\n;}文件目录操作一般来说常见的目录操作不外乎创建、拷贝、重命名、删除目录、遍历当前或递归遍历当前目录等等看代码#includecassert#includecstdlib#includefilesystemintmain(){std::filesystem::current_path(std::filesystem::temp_directory_path());// Basic usagestd::filesystem::create_directories(sandbox/1/2/a);std::filesystem::create_directory(sandbox/1/2/b);// Directory already exists (false returned, no error)assert(!std::filesystem::create_directory(sandbox/1/2/b));std::coutdirectory_iterator:\n;// directory_iterator can be iterated using a range-for loopfor(autoconstdir_entry:std::filesystem::directory_iterator{sandbox})std::coutdir_entry.path()\n;std::cout\ndirectory_iterator as a range:\n;// directory_iterator behaves as a range in other ways, toostd::ranges::for_each(std::filesystem::directory_iterator{sandbox},[](constautodir_entry){std::coutdir_entry\n;});std::cout\nrecursive_directory_iterator:\n;for(autoconstdir_entry:std::filesystem::recursive_directory_iterator{sandbox})std::coutdir_entry\n;fs::create_directories(sandbox/dir/subdir);std::ofstream(sandbox/file1.txt).put(a);fs::copy(sandbox/file1.txt,sandbox/file2.txt);// copy filefs::copy(sandbox/dir,sandbox/dir2);// copy directory (non-recursive)constautocopyOptionsfs::copy_options::update_existing|fs::copy_options::recursive|fs::copy_options::directories_only;fs::copy(sandbox,sandbox_copy,copyOptions);static_castvoid(std::system(tree));// Permissions copying usagestd::filesystem::permissions(sandbox/1/2/b,std::filesystem::perms::others_all,std::filesystem::perm_options::remove);std::filesystem::create_directory(sandbox/1/2/c,sandbox/1/2/b);std::system(ls -l sandbox/1/2);std::system(tree sandbox);std::filesystem::remove_all(sandbox);}通过上述操作可以进行文件目录的过滤、修改和文件处理等功能操作。属性操作文件属性的操作也是非常常见的包括前面提到的Space空间处理其还有很多如类型、时间、状态及大小等等相关可参看上面的那个图中的相关属性。namespace fsstd::filesystem;voiddemo_status(constfs::pathp,fs::file_status s){std::coutp;switch(s.type()){casefs::file_type::none:std::cout has not-evaluated-yet type;break;casefs::file_type::not_found:std::cout does not exist;break;casefs::file_type::regular:std::cout is a regular file;break;casefs::file_type::directory:std::cout is a directory;break;casefs::file_type::symlink:std::cout is a symlink;break;default:std::cout has implementation-defined type;break;}std::cout\n;}voiddemo_exists(constfs::pathp,fs::file_status sfs::file_status{}){std::coutp;if(fs::status_known(s)?fs::exists(s):fs::exists(p))std::cout exists\n;elsestd::cout does not exist\n;}intmain(){// create files of different kindsfs::create_directory(sandbox);fs::create_directory(sandbox/dir);std::ofstream{sandbox/file};// create regular filefs::create_symlink(file,sandbox/symlink);// demo different status accessorsfor(autoit{fs::directory_iterator(sandbox)};it!fs::directory_iterator();it)demo_status(*it,it-symlink_status());// use cached status from directory entrydemo_status(/dev/null,fs::status(/dev/null));// direct calls to statusdemo_status(/dev/sda,fs::status(/dev/sda));demo_status(sandbox/no,fs::status(/sandbox/no));demo_exists(sandbox);// cleanup (prefer std::unique_ptr-based custom deleters)close(fd);fs::remove_all(sandbox);}更多的属性操作请参看具体的cppreference中的相关文档说明。符号链接操作处理符号链接是一个比较突出的问题这个在Linux平台上可能更容易遇到。namespace fsstd::filesystem;intmain(){fs::create_directories(sandbox/subdir);fs::create_symlink(target,sandbox/sym1);fs::create_directory_symlink(subdir,sandbox/sym2);for(autoitfs::directory_iterator(sandbox);it!fs::directory_iterator();it)if(is_symlink(it-symlink_status()))std::cout*it-read_symlink(*it)\n;assert(std::filesystem::equivalent(sandbox/sym2,sandbox/subdir));std::ofstream(sandbox/a).put(a);// create regular filefs::create_hard_link(sandbox/a,sandbox/b);fs::remove(sandbox/a);// read from the original file via surviving hard linkcharcstd::ifstream(sandbox/b).get();std::coutc\n;fs::remove_all(sandbox);for(fs::path p:{/usr/bin/gcc,/bin/cat,/bin/mouse}){std::coutp;fs::exists(p)?fs::is_symlink(p)?std::cout - fs::read_symlink(p)\n:std::cout exists but it is not a symlink\n:std::cout does not exist\n;}}错误处理这个就是一种安全机制了也可以算作异常的处理#includefilesystem#includeiostream#includesystem_errorintmain(){conststd::filesystem::path from{/none1/a},to{/none2/b};try{std::filesystem::copy_file(from,to);// throws: files do not exist}catch(std::filesystem::filesystem_errorconstex){std::coutwhat(): ex.what()\npath1(): ex.path1()\npath2(): ex.path2()\ncode().value(): ex.code().value()\ncode().message(): ex.code().message()\ncode().category(): ex.code().category().name()\n;}// All functions have non-throwing equivalentsstd::error_code ec;std::filesystem::copy_file(from,to,ec);// does not throwstd::cout\nNon-throwing form sets error_code: ec.message()\n;}辅助功能这个也比较多比如绝对和规范路径的处理absolutecanonical接口临时路径等等namespace fsstd::filesystem;voidshow(std::filesystem::path x,std::filesystem::path y){std::coutx:\t\t x\ny:\t\t y\nrelative(x, y): std::filesystem::relative(x,y)\nproximate(x, y): std::filesystem::proximate(x,y)\n\n;}intmain(){std::filesystem::path pfoo.c;std::coutCurrent path is std::filesystem::current_path()\n;std::coutAbsolute path for p is fs::absolute(p)\n;show(/a/b/c,/a/b);show(/a/c,/a/b);show(c,/a/b);show(/a/b,c);}说明以上代码均来自cppreference四、常见问题虽然获得了使用filesystem的便利性但也不得不考虑在应用时的一些问题的异常行为。主要包括权限处理和使用filesystem提供的perms权限处理有点简陋如果要在Linux上进行复杂的权限操作则无法满足仍然需要调用底层的接口。另外在使用其时也要注意有无权限操作相关文件的权限问题否则可能导致操作失败跨平台的路径格式在不同的平台路径分隔符可能有所不同如Windows平台使用“\”而Linux使用“/”所以在直接操作时要注意。另外Windows平台和Linux平台的分区格式、路径长度Windows限制为260字符Win10后支持长路径也不相同都需要引起注意。一般建议是始终使用“/”让path自行转换相关平台应用符号链接的问题在操作符号链接时问题就比较多了首先要确认到底想操作链接还是源目标不要搞错。另外还要处理空链接源不存在仍然创建成功创建问题硬链接创建的条件无法创建目录的硬链接等循环链接。另外要弄清楚read_symlink与canonical的不同前者只取单纯指向的链接源不管源是否再指向其它而后者则会找到最后的真实文件文件名称的大小写问题这个一定要高度注意很多从Windows转Linux上的吃过大亏。前者是不区分大小写的而后者严格区分。特别是O等几个不太明显的字符一定要小心性能问题在实际的应用中可能会对一个目录进行递归遍历或者拷贝一个很大的文件夹这都可能导致性能的下降。需要开发者自行处理类似的情况更多的具体的细节问题可以在使用filesystem的接口时查看相关的说明。还是写得相当清楚的。五、总结俗话说的好大路不平旁人铲。自己不产别人产。从现在AI辅助编程工具的发展看再过几年可能就不是单纯的标准向着自然语言转了而是直接由AI创建自然语言接口然后纯口语编程了。
跟我学c++中级篇—c++17的filesystem主要功能
一、文件处理其实开发者在实际的开发中都会发现对文件IO的操作是相当麻烦的。如果只是在单一的平台上开发可能还稍微好一些只需处理本平台相关的操作接口就可以了。但是又发现几乎所有的对文件或文件夹的操作都存在着一个共性那就是相关的接口以及数据结构都相当的复杂。大家当然可以理解为了兼容内核的底层库必须要把相关的细节暴露出来的设计理念。但对于更多的应用者来说其实有很多的细节意义并不大。比如在前面Linux的数据结构“struct statvfs”可能很多开发者见而生畏。二、std::filesystem随着技术的发展跨平台的需求越来越多而C的标准也在不断在迭代。在C17的标准中提供了一个filesystem库它解决了原有接口不统一、跨平台不方便不兼容以及数据结构复杂的痛点。特别是通过分解相关功能利用不同的模块来处理文件相关的各类属性让上层应用者不必关心过多的文件操作细节。同时它还支持Unicode、软硬链接及权限管理等操作为关心细节操作的开发者提供了更方便的接口操作方法。有过Boost开发经验的很可能就会想到Boost中的Filesystem没错。不然怎么会把Boost称为标准库的预备库呢filesystem的主要相关内容如下可以发现其包含的相关的内容还是相当多的。不过对于普通开发者最方便的就是解决了文件路径操作的不方便不用自己再手动的来回来去的组装和拆解文件路径了。三、应用下面对filesystem的主要用法进行分析和说明文件路径操作这可以说是开发者的福音其引入的filesystem::path提供了路径字符串的拆分、组合及相关的解析等。最典型的就是直接从全路径中获取文件名等#includefilesystem#includeiostreamnamespace fsstd::filesystem;intmain(){std::coutfs::path(/foo/bar.txt).filename()\n;std::coutfs::path(/foo).replace_filename(bar)\n;fs::path p;std::coutstd::boolalpha(pfoo/bar).remove_filename()\tp.has_filename()\n;std::coutfs::path(/foo/bar.txt).extension()\n;for(constfs::path p:{/foo/bar.txt,/foo/.bar,foo.bar.baz.tar})std::coutpath: p, stem: p.stem()\n;std::cout\n;for(fs::path pfoo.bar.baz.tar;!p.extension().empty();pp.stem())std::coutpath: p, extension: p.extension()\n;}文件目录操作一般来说常见的目录操作不外乎创建、拷贝、重命名、删除目录、遍历当前或递归遍历当前目录等等看代码#includecassert#includecstdlib#includefilesystemintmain(){std::filesystem::current_path(std::filesystem::temp_directory_path());// Basic usagestd::filesystem::create_directories(sandbox/1/2/a);std::filesystem::create_directory(sandbox/1/2/b);// Directory already exists (false returned, no error)assert(!std::filesystem::create_directory(sandbox/1/2/b));std::coutdirectory_iterator:\n;// directory_iterator can be iterated using a range-for loopfor(autoconstdir_entry:std::filesystem::directory_iterator{sandbox})std::coutdir_entry.path()\n;std::cout\ndirectory_iterator as a range:\n;// directory_iterator behaves as a range in other ways, toostd::ranges::for_each(std::filesystem::directory_iterator{sandbox},[](constautodir_entry){std::coutdir_entry\n;});std::cout\nrecursive_directory_iterator:\n;for(autoconstdir_entry:std::filesystem::recursive_directory_iterator{sandbox})std::coutdir_entry\n;fs::create_directories(sandbox/dir/subdir);std::ofstream(sandbox/file1.txt).put(a);fs::copy(sandbox/file1.txt,sandbox/file2.txt);// copy filefs::copy(sandbox/dir,sandbox/dir2);// copy directory (non-recursive)constautocopyOptionsfs::copy_options::update_existing|fs::copy_options::recursive|fs::copy_options::directories_only;fs::copy(sandbox,sandbox_copy,copyOptions);static_castvoid(std::system(tree));// Permissions copying usagestd::filesystem::permissions(sandbox/1/2/b,std::filesystem::perms::others_all,std::filesystem::perm_options::remove);std::filesystem::create_directory(sandbox/1/2/c,sandbox/1/2/b);std::system(ls -l sandbox/1/2);std::system(tree sandbox);std::filesystem::remove_all(sandbox);}通过上述操作可以进行文件目录的过滤、修改和文件处理等功能操作。属性操作文件属性的操作也是非常常见的包括前面提到的Space空间处理其还有很多如类型、时间、状态及大小等等相关可参看上面的那个图中的相关属性。namespace fsstd::filesystem;voiddemo_status(constfs::pathp,fs::file_status s){std::coutp;switch(s.type()){casefs::file_type::none:std::cout has not-evaluated-yet type;break;casefs::file_type::not_found:std::cout does not exist;break;casefs::file_type::regular:std::cout is a regular file;break;casefs::file_type::directory:std::cout is a directory;break;casefs::file_type::symlink:std::cout is a symlink;break;default:std::cout has implementation-defined type;break;}std::cout\n;}voiddemo_exists(constfs::pathp,fs::file_status sfs::file_status{}){std::coutp;if(fs::status_known(s)?fs::exists(s):fs::exists(p))std::cout exists\n;elsestd::cout does not exist\n;}intmain(){// create files of different kindsfs::create_directory(sandbox);fs::create_directory(sandbox/dir);std::ofstream{sandbox/file};// create regular filefs::create_symlink(file,sandbox/symlink);// demo different status accessorsfor(autoit{fs::directory_iterator(sandbox)};it!fs::directory_iterator();it)demo_status(*it,it-symlink_status());// use cached status from directory entrydemo_status(/dev/null,fs::status(/dev/null));// direct calls to statusdemo_status(/dev/sda,fs::status(/dev/sda));demo_status(sandbox/no,fs::status(/sandbox/no));demo_exists(sandbox);// cleanup (prefer std::unique_ptr-based custom deleters)close(fd);fs::remove_all(sandbox);}更多的属性操作请参看具体的cppreference中的相关文档说明。符号链接操作处理符号链接是一个比较突出的问题这个在Linux平台上可能更容易遇到。namespace fsstd::filesystem;intmain(){fs::create_directories(sandbox/subdir);fs::create_symlink(target,sandbox/sym1);fs::create_directory_symlink(subdir,sandbox/sym2);for(autoitfs::directory_iterator(sandbox);it!fs::directory_iterator();it)if(is_symlink(it-symlink_status()))std::cout*it-read_symlink(*it)\n;assert(std::filesystem::equivalent(sandbox/sym2,sandbox/subdir));std::ofstream(sandbox/a).put(a);// create regular filefs::create_hard_link(sandbox/a,sandbox/b);fs::remove(sandbox/a);// read from the original file via surviving hard linkcharcstd::ifstream(sandbox/b).get();std::coutc\n;fs::remove_all(sandbox);for(fs::path p:{/usr/bin/gcc,/bin/cat,/bin/mouse}){std::coutp;fs::exists(p)?fs::is_symlink(p)?std::cout - fs::read_symlink(p)\n:std::cout exists but it is not a symlink\n:std::cout does not exist\n;}}错误处理这个就是一种安全机制了也可以算作异常的处理#includefilesystem#includeiostream#includesystem_errorintmain(){conststd::filesystem::path from{/none1/a},to{/none2/b};try{std::filesystem::copy_file(from,to);// throws: files do not exist}catch(std::filesystem::filesystem_errorconstex){std::coutwhat(): ex.what()\npath1(): ex.path1()\npath2(): ex.path2()\ncode().value(): ex.code().value()\ncode().message(): ex.code().message()\ncode().category(): ex.code().category().name()\n;}// All functions have non-throwing equivalentsstd::error_code ec;std::filesystem::copy_file(from,to,ec);// does not throwstd::cout\nNon-throwing form sets error_code: ec.message()\n;}辅助功能这个也比较多比如绝对和规范路径的处理absolutecanonical接口临时路径等等namespace fsstd::filesystem;voidshow(std::filesystem::path x,std::filesystem::path y){std::coutx:\t\t x\ny:\t\t y\nrelative(x, y): std::filesystem::relative(x,y)\nproximate(x, y): std::filesystem::proximate(x,y)\n\n;}intmain(){std::filesystem::path pfoo.c;std::coutCurrent path is std::filesystem::current_path()\n;std::coutAbsolute path for p is fs::absolute(p)\n;show(/a/b/c,/a/b);show(/a/c,/a/b);show(c,/a/b);show(/a/b,c);}说明以上代码均来自cppreference四、常见问题虽然获得了使用filesystem的便利性但也不得不考虑在应用时的一些问题的异常行为。主要包括权限处理和使用filesystem提供的perms权限处理有点简陋如果要在Linux上进行复杂的权限操作则无法满足仍然需要调用底层的接口。另外在使用其时也要注意有无权限操作相关文件的权限问题否则可能导致操作失败跨平台的路径格式在不同的平台路径分隔符可能有所不同如Windows平台使用“\”而Linux使用“/”所以在直接操作时要注意。另外Windows平台和Linux平台的分区格式、路径长度Windows限制为260字符Win10后支持长路径也不相同都需要引起注意。一般建议是始终使用“/”让path自行转换相关平台应用符号链接的问题在操作符号链接时问题就比较多了首先要确认到底想操作链接还是源目标不要搞错。另外还要处理空链接源不存在仍然创建成功创建问题硬链接创建的条件无法创建目录的硬链接等循环链接。另外要弄清楚read_symlink与canonical的不同前者只取单纯指向的链接源不管源是否再指向其它而后者则会找到最后的真实文件文件名称的大小写问题这个一定要高度注意很多从Windows转Linux上的吃过大亏。前者是不区分大小写的而后者严格区分。特别是O等几个不太明显的字符一定要小心性能问题在实际的应用中可能会对一个目录进行递归遍历或者拷贝一个很大的文件夹这都可能导致性能的下降。需要开发者自行处理类似的情况更多的具体的细节问题可以在使用filesystem的接口时查看相关的说明。还是写得相当清楚的。五、总结俗话说的好大路不平旁人铲。自己不产别人产。从现在AI辅助编程工具的发展看再过几年可能就不是单纯的标准向着自然语言转了而是直接由AI创建自然语言接口然后纯口语编程了。