C++
语言(实际上是C++
标准)最常见的问题之一是缺少定义明确的库来帮助处理文件系统查询和操作。 这种缺席导致程序员使用本机操作系统提供的应用程序接口(API),从而使代码无法跨平台移植。 考虑一个简单的情况:您需要确定文件是否为目录类型。 在Microsoft®Windows®平台中,可以通过调用windows.h头文件中定义的GetAttributes
库函数来执行此操作:
DWORD GetFileAttributes (LPCTSTR lpFileName);
对于目录,结果应为FILE_ATTRIBUTE_DIRECTORY,并且您的代码必须进行检查。 在UNIX®和Linux®平台上,可以使用stat
或fstat
例程以及sys / stat.h中定义的S_ISDIR宏来实现相同的功能。 您还必须了解stat
结构。 这是相应的代码:
#include <sys/stat.h>
#include <stdio.h>
int main()
{
struct stat s1;
int status = stat(<const char* denoting pathname>, &s1);
printf(“Path is a directory : %d\n”, S_ISDIR(s1.st_mode));
return 0;
}
对于具有重型I / O的程序,这种不一致意味着在跨平台移植代码方面需要进行大量的工程设计工作。 因此,您将介绍Boost Filesystem库。 这个广泛使用的库为执行文件系统操作提供了安全,可移植且易于使用的C++
接口。 可从Boost网站免费下载。
您的第一个使用boost :: filesystem的程序
在深入研究Boost Filesystem库的细微差别之前, 清单1显示了使用Boost API判断文件是否为Directory类型的代码。
清单1.确定文件是否为目录类型的代码
#include <stdio.h>
#include “boost/filesystem.hpp”
int main()
{
boost::filesystem::path path("/usr/local/include"); // random pathname
bool result = boost::filesystem::is_directory(path);
printf(“Path is a directory : %d\n”, result);
return 0;
}
该代码是不言自明的,您不需要了解任何特定于系统的例程。 验证该代码可以在gcc-3.4.4和cl-13.10.3077上进行编译,而无需进行修改。
了解Boost路径对象
理解Boost文件系统库的关键是path
对象,因为在文件系统库中定义的几个例程仅旨在与适当的path
对象一起使用。 通常,文件系统路径取决于操作系统。 例如,众所周知,UNIX和Linux系统使用virgule( /
)字符作为目录分隔符,而Windows使用反斜杠( \
)来实现类似目的。 boost::filesystem::path
旨在精确抽象该功能。 path
对象可以通过多种方式初始化,最常见的是使用char*
或std::string
进行初始化,如清单2所示。
清单2.创建Boost路径对象的方法
path(); // empty path
path(const char* pathname);
path(const std::string& pathname);
path(const char* pathname, boost::filesystem::path::name_check checker);
path(const std::string& pathname, boost::filesystem::path::name_check checker);
在初始化path
对象时,可以以本机格式或可移植操作系统接口(POSIX)委员会定义的可移植格式提供PATHNAME变量。 两种方法在实践中都有其相对的权衡。 考虑一种情况,您要处理软件创建的目录,然后在UNIX和Linux系统上的/ tmp / mywork中以及在Windows上的C:\ tmp \ mywork中找到该目录。 有几种方法可以解决此问题。 清单3显示了本机面向格式的方法。
清单3.使用本机格式初始化路径
#ifdef UNIX
boost::filesystem::path path("/tmp/mywork");
#else
boost::filesystem::path path("C:\\tmp\\mywork ");
#endif
需要一个#ifdef
来基于每个操作系统初始化路径。 但是,如果您更喜欢使用可移植格式,请查看清单4 。
清单4.使用可移植格式初始化路径
boost::filesystem::path path("/tmp/mywork");
请注意, path::name_check
是指名称检查功能原型。 如果名称检查函数的参数输入PATHNAME对于特定的操作系统或文件系统有效,则该函数将返回“ True”。 Boost Filesystem Library随附了几个名称检查功能,也欢迎您提供自己的变体。 一些更常用的名称检查功能是Boost提供的portable_posix_name
和windows_name
。
路径成员功能概述
path
对象带有几种成员方法。 这些成员例程不会修改文件系统,但会根据路径名提供有用的信息。 本节概述了其中一些例程:
-
const std::string& string( )
:此例程返回初始化路径的字符串的副本,并按照路径语法规则进行格式化。 -
std::string root_directory( )
:给定路径,此API将返回根目录; 否则,它将返回一个空字符串。 例如,如果路径由/ tmp / var1组成,则此例程将返回/
,它是UNIX文件系统的根。 但是,如果路径是相对路径,例如../mywork/bin,则此例程将返回空字符串。 -
std::string root_name( )
:给定一个从文件系统的根开始的路径,该例程返回一个字符串,其中包含PATHNAME的第一个字符。 -
std::string leaf( )
:给定绝对路径名(例如/ home / user1 / file2),此例程仅提供与文件名相对应的字符串(即file2)。 -
std::string branch_path( )
:这是leaf
的补充例程。 给定一个路径,它将返回除最后一个以外的所有用于构造路径的元素。 例如,对于使用/ a / b / c初始化的路径,path.branch_path( )
返回/a/b
。 对于具有单个元素(例如c)的路径,此例程返回一个空字符串。 -
bool empty( )
:如果路径对象包含一个空字符串(例如, 路径path1(“”)),则此例程返回True或False。 -
boost::filesystem::path::iterator
:此例程用于遍历路径的各个元素。 考虑清单5 。清单5.使用path :: iterator(开始和结束接口)
#include <iostream> #include “boost/filesystem.hpp” int main() { boost::filesystem::path path1("/usr/local/include"); // random pathname boost::filesystem::path::iterator pathI = path1.begin(); while (pathI != path1.end()) { std::cout << *pathI << std::endl; ++pathI; } return 0; } // result: 1
上面程序的输出依次是
/
,usr
,local
,include
,表示目录层次结构。 -
path operator / (char* lhs, const path& rhs)
:此例程是path
的非成员函数。 它返回使用lhs
和rhs
形成的路径的串联。 它自动插入/
作为路径分隔符,如清单6所示。清单6.路径字符串的串联
#include <iostream> #include "boost/filesystem.hpp" int main() { boost::filesystem::path path1("local/include"); boost::filesystem::path path2 = operator/("/usr", path1); std::cout << "Path: " << path2 << std::endl; return 0; } // result: /usr/local/include
错误处理
文件系统操作经常遇到意外问题,Boost Filesystem Library使用C++
异常报告运行时错误。 boost::filesystem_error
类是从std::runtime_error
类派生的。 库中的函数使用filesystem_error
异常来报告操作错误。 对应于可能的不同错误类型,Boost标头中定义了错误代码。 用户代码通常位于try...catch
块中,该块使用filesystem_error
异常报告相关的错误消息。 清单7提供了一个重命名文件的小例子,除了from
路径中的文件不存在。
清单7. Boost中的错误处理
#include <iostream>
#include “boost/filesystem.hpp”
int main()
{
try {
boost::filesystem::path path("C:\\src\\hdbase\\j1");
boost::filesystem::path path2("C:\\src\\hdbase\\j2");
boost::filesystem::rename(path, path2);
}
catch(boost::filesystem::filesystem_error e) {
// do the needful
}
return 0;
}
Boost Filesystem库中的功能类别
boost::filesystem
提供了不同的功能类别:有些函数(例如is_directory
用于查询文件系统,而另一些函数(例如create_directory
)则主动对其进行修改。 根据其功能,功能大致分为以下几类:
- 属性功能:提供其他信息,例如文件大小和磁盘使用情况。
- 文件系统操作功能:旨在创建常规文件,目录和符号链接; 复制和重命名文件; 并提供删除。
- 实用程序/谓词功能:测试文件是否存在,等等。
- 其他便利功能:以编程方式更改文件扩展名,依此类推。
属性功能
Boost Filesystem库由以下属性函数组成:
-
uintmax_t file_size(const path&)
:以字节为单位返回常规文件的大小 -
boost::filesystem::space_info space(const path&)
:获取路径并返回定义为以下内容的space_info
结构:struct space_info { uintmax_t capacity; uintmax_t free; uintmax_t available; };
基于文件系统所属的磁盘分区,此例程为该分区中的所有目录返回相同的磁盘使用情况统计信息(以字节为单位)。 例如,C:\ src \ dir1和C:\ src \ dir2都将返回相同的磁盘使用数据。
-
std::time_t last_write_time(const path&)
:返回文件的最后修改时间。 -
void last_write_time(const path&, std::time_t new_time)
:修改文件的最后修改时间。 -
const path& current_path( )
:返回程序当前工作目录的完整路径(请注意,此路径可能与程序最初运行的目录不同,因为可以通过编程方式更改目录。)
文件系统操作功能
这组函数负责创建新文件和目录,删除文件,等等:
-
bool create_directory(const path&)
:此函数创建具有给定路径名的目录。 (请注意,如果PATHNAME本身由无效字符组成,则结果通常是在平台上定义的。例如,在UNIX和Windows系统中,星号(*
),问号(?
)和其他此类字符都被视为无效字符,并且不能在目录名称中。) -
bool create_directories(const path&)
:您可以使用此API创建目录树,而不是单个目录。 例如,考虑目录树/ a / b / c,该目录树必须在/ tmp文件夹中创建。 调用此API即可完成任务,而具有相同参数的create_directory
会引发异常。 -
bool create_hard_link (const path& frompath, const path& topath)
:此函数在frompath
和topath
之间创建硬链接。 -
bool create_symlink(const path& frompath, const path& topath)
:此函数在frompath
和topath
之间创建符号(软)链接。 -
void copy_file(const path& frompath, const path& topath)
该文件的内容和属性由称为frompath
被复制到该文件通过称为topath
。 该例程期望目标文件不存在 。 如果目标文件存在,则抛出异常。 因此,这不等同于UNIX中系统指定的cp
命令。 还期望frompath
变量将引用适当的常规文件。 考虑以下示例:frompath
符号链接/ tmp / file1,而该链接又引用文件/ tmp / file2; 例如,topath
是/ tmp / file3。 在这种情况下,copy_file
将失败。 与cp
命令相比,此API还有另一个区别。 -
void rename(const path& frompath, const path& topath)
:此函数是用于重命名文件的API。 通过在topath
参数中指定完整路径名,也可以同时重命名和更改文件的位置,如清单8所示。清单8. Boost中的重命名功能
#include <stdio.h> #include “boost/filesystem.hpp” int main() { boost::filesystem::path path("/home/user1/abc"); boost::filesystem::rename(path, "/tmp/def"); return 0; } // abc is renamed def and moved to /tmp folder
-
bool remove(const path& p)
:此例程尝试删除路径p引用的文件或目录。 对于目录,如果目录的内容尚未为空,则此例程将引发异常。 注意:该例程并不关心它要删除的内容,即使其他程序同时访问了同一文件也是如此! -
unsigned long remove_all(const path& p)
:此API尝试删除路径p引用的文件或目录。 与remove
不同,它对不为空的目录不作任何特殊考虑。 该功能与UNIXrm –rf
命令的Boost等效。
效用和谓词功能
Boost Filesystem库包含以下实用程序和谓词功能:
-
bool exists(const path&)
:此函数检查文件是否存在。 该文件可以是任何东西:常规文件,目录,符号链接等。 -
bool is_directory(const path&)
:此函数检查路径是否对应于目录。 -
bool is_regular(const path&)
:此函数检查常规文件(即目录,符号链接,套接字或设备文件)。 -
bool is_other(const path&)
:通常,此函数检查设备文件,例如/ dev / tty0或套接字文件。 -
bool is_empty(const path&)
:如果路径对应于文件夹,则此函数检查文件夹是否为空,并相应地返回True。 如果路径对应于文件,则此函数检查文件大小是否等于0。在硬链接或符号链接到文件的情况下,此API检查原始文件是否为空。 -
bool equivalent(const path1& p1, const path2& p2)
:这个极其有用的API比较了相对样式和绝对样式的路径名。 考虑清单9 :清单9.测试两条路径的等效性
#include <stdio.h> #include “boost/filesystem.hpp” int main() { boost::filesystem::path path1("/usr/local/include"); // random pathname boost::filesystem::path path2("/tmp/../usr/local/include"); bool result = boost::filesystem::is_equivalent(path1, path2); printf(“Paths are equivalent : %d\n”, result); return 0; } // result: 1
-
path system_complete(const path&)
:此函数是另一个与bool equivalent(const path1& p1, const path2& p2)
APIbool equivalent(const path1& p1, const path2& p2)
。 给定当前工作目录中的任意文件路径,此API返回文件的绝对路径名。 例如,如果用户在目录/ home / user1中,并查询文件../user2/file2,则此函数返回/home/user2/file2
,这是文件file2的完整路径名。
杂项功能
Boost Filesystem库包含以下杂项功能:
-
std::string extension(const path&)
:此函数返回以句点(.
)为前缀的给定文件名的扩展名。 例如,对于名称为test.cpp的文件,extension
返回.cpp
。 如果文件没有扩展名,该函数将返回一个空字符串。 对于隐藏文件(即,在UNIX系统中名称以.
开头的文件),该函数将适当地计算扩展名类型或返回空字符串(因此,对于.test.profile,此例程返回.profile
)。 -
std::string basename(const path&)
:这是extension
的补充例程:它返回字符串before.
在文件名中。 注意,即使在提供绝对文件名的情况下,此API仍仅返回直接构成文件名一部分的字符串,如清单10所示。清单10.使用boost :: basename
#include <stdio.h> #include <cstring> #include “boost/filesystem.hpp” use namespace std; int main() { boost::filesystem::path path1("/tmp/dir1/test1.c "); boost::filesystem::path path2("/tmp/dir1/.test1.profile"); string result1 = boost::filesystem::basename (path1); string result2 = boost::filesystem::basename (path2); printf(“Basename 1: %s Basename2 : %s\n”, result1.c_str(), result2.c_str()); return 0; } // result: Basename1: test1 Basename2: .test1
-
std::string change_extension(const path& oldpath, const std::string new_extension)
:此API返回一个新字符串,该字符串反映了更改后的名称。 请注意,与oldpath
对应的文件保持不变 。 这只是一个便利功能。 另请注意,您必须在扩展名中明确指定点 。 例如,change_extension("test.c", "so")
导致testso
而不是test.so
结论
本文简要介绍了Boost Filesystem库。 不应将其视为Boost中整个文件系统接口的全面文档。 没有讨论API集的内部,也没有讨论这些API在非UNIX或Windows平台(例如VMS)中的微妙之处。 有关文件系统的更多信息,请参阅参考资料 。
翻译自: https://www.ibm.com/developerworks/aix/library/au-boostfs/index.html