上一讲文明说了关于path和status,那么这一讲我们来说说文件属性,我们只说一些boost里面提供的文件属性,而因为各平台有不同的定义,其实boost里也没有提供得有多少关于文件属性的操作,好吧,不管怎么样,还是和大家聊聊。
boost::filesystem::initial_path();
该函数返回的是程序启动的当前路径,也就是是exe所在的文件路径,和这个函数有同样功能的是另一个函数:
boost::filesystem::current_path();
他们返回的两个路径都是程序的绝对路径,所以在空参数时他们返回的都是同一个路径;
boost::filesystem::file_size();
该函数返回的是一个文件的大小,以字节为单位,这个函数其实相当有用的,比如我们通常会从一个二进制文件中读取数据,有时候我们可能想直接全部读取出来再慢慢分离,但是当我们知道二进制文件中储存的格式之后我们可以直接一下全把数据读取出来,比如,有这么一个文件,他的格式如下:
//==================================================
[int,int,[int,double,double]{55}]{n}
[]里面是一个块,{}表示改块重复的次数
//==================================================
当我们拿到这个文件的时候,最常用的办法是一个个的取出来,也可以一下子取出来,再使用类型转换,但是当我们有file_size这个函数的时候我们可以有一个更为有效的方式来读这个二进制文:
//===================================================
static const int NUM{55};
#pragma pack(1)
struct Freq{
int __ff;
double __ample;
double __phase;
};
struct __Data{
int __i;
int __q;
Freq __f[NUM];
};
#pragma pack()
这里的数据结构我们必须指定为1字节对齐,为什么呢?因为这样可以让他像char一样来工作(内存模型);
在我们定义好这个数据结构之后,我们就可以查看文件的大小,然后通过比较得得该文件中到底储存了多少个这样的结果
__int64 size = boost::filesystem::file_size(filename);
int __num = size / sizeof(__Data);
__Data* m_data = new __Data[__num]; //创建一个大数据块
inFile.read((char*)(m_data), size); //将数据整块读取
inFile.close();
//=====================================================
只要知道文件的储存格式,使用file_size函数辅助要都文件就变得相当的简单,我们甚至不需要判断是否到文件的eof就能够提取我们想要的数据(上面的关键点就是在于将数据结构声明为1字节对齐,当然如果不这么做的话,可以使用char*直接读取,方法如下):
//=====================================================
static const int NUM{55};
__int64 size = boost::filesystem::file_size(filename);
char* m_data = new char[size]; //创建一个大数据块
inFile.read(m_data, size); //将数据整块读取
inFile.close();
接下来我们需要各种转换才能得到我们想要的结果:
int __i{0},__q{0};
int __f{0};
double __ample{0.0},__phase{0.0};
int i{0};
for(;i<size;){
__i = *(int*)(&m_data[i]);
i += sizeof(int);
__q = *(int*)(&m_data[i]);
i += sizeof(int);
for(int j=0;j<NUM;++j){
__f = *(int*)(&m_data[i]);
i += sizeof(int);
__ample = *(double*)(&m_data[i]);
i += sizeof(double);
__phase = *(double*)(&m_data[i]);
i += sizeof(double);
// 将数据保存在相应的数据结构中去
}
}
//===================================================
和定义一个一字节对齐的数据结构比起,直接转换的操作变得相对繁琐和可读性差,而且更重要的是很多人想不起将char*的内容转换到对应的正确类型的正确方法(因为我曾在一道面试题中靠过这中转换方式,遗憾的是来面试的人基本在这题上都挂了)。
哦……好像扯远了,我们继续回归主题吧。
boost::filesystem::last_write_time();
该函数可以用来获取一个文件的最后修改时间,也可以用来设置一个文件的最后修改时间,返回的是一个time_t结果,设置最后修改时间需要一个路径,和一个时间点为参数,通常我们使用time(0)来将文件的最后修改时间设置当前时间。
boost::filesystem::space();
该函数主要是获取指定路径下面的磁盘空间分配情况,他返回一个space_info结构,该结构的定义如下:
//==================================================
struct space_info{
unitmax_t capacity;
unitmax_t free;
unitmax_t available;
};
//=================================================
除了这些外,filesystem还提供一些有用的文件操作函数:
boost::filesystem::create_directory();
该函数用于创建目录;
boost::filesystem::rename();
该函数用于更改文件名。
boost::filesystem::copy_file();
该函数用于拷贝文件,还有remove()删除文件等等,不过remove只能删除文件或者空的文件夹,如果我们要想删除一个有东西的目录的话,应该使用remove_all(),该函数会递归删除文件,下面展示一下这些函数的使用方法:
//=============================================
boost::filesystem::path p("I:/Test1");
assert(boost::filesystem::exist(p));
if(boost::filesystem::is_empty(p)){
boost::filesystem::remove(p);
}
else{
boost::filesystem::remove_all(p);
}
assert(!boost::filesystem::exist(p));
boost::filesystem::create_directory(p);
assert(boost::filesystem::exist(p));
// 将I:/Test/main.cpp拷贝到p/"main.cpp"中
boost::filesystem::copy_file("I:/Test/main.cpp",p/"main.cpp");
assert(boost::filesystem::exist(p/"main.cpp");
// 改名
boost::filesystem::rename(p/"main.cpp",p/"a.cpp");
// 创建多节目录,这个操作是相当有用的
// 这属于递归创建目录,我们接下来可以使用
// create_directory来实现这个递归创建目录的小函数
boost::filesystem::create_directories(p/"sub_1"/"sub_2");
//================================================
那么接下来大家可能比较关心目录的遍历,filesystem里面提供了目录的遍历方式,他和我们使用迭代器操作一样简单,所以他的目录迭代类名字就叫做:directory_iterator,不过他和我们标准库容器提供的容器的迭代器又有稍许的不同,他和regex库里面的regex_token_iterator一样,使用空的构造函数生成一个end迭代器,也就是我们用的最多的那个无效迭代器,但是又必须存在的end。所以要迭代一个目录最简单的方式就是:
//================================================
typdefe boost::filesystem::directory_iterator dir_it;
dir_it end;
for(dir_it beg("I:/Test1");beg!=end;++beg){
std::cout<<*beg<<std::endl;
}
//================================================
不过directory_iterator不具有递归性,他只会将指定的目录中的信息迭代出来,不会继续往下追,当时我们先不要遗憾,因为考虑到这种递归迭代操作的有用性,filesystem也提供了递归目录迭代的操作:recursive_directory_iterator,该类提供有几个有用的成员函数:
int level() const;
该函数返回当前的迭代的目录深度,原始第一层目录为0,往下进一层+1,出一层-1.
void pop() ;
该函数是推出当前目录的遍历,比如我们查找文件的时候,我们可能会知道该文件不会存在某些目录之中,也有可能我们不希望别人访问到该目录,所以当我们发现遍历到我们不想遍历的目录时,我们只用pop函数退出当前目录的遍历。
void no_push();
该函数的功能是不遍历目录,也就是说使用该函数后就相当directory_iterator了。
下面同样展示一下他的用法:
//==============================================
typedef recursive_directory_iterator rd_it;
rd_it end;
for(rd_it beg("I:/Test1");beg!=end;++beg){
std::cout<<*beg<<std::endl;
}
//下面让他们像directory_iterator一样工作
rd_it end;
for(rd_it beg("I:/Test1");beg!=end;++beg){
if(boost::filesystem::is_directory(*beg)){
beg.no_push();
}
std::cout<<*beg<<std::endl;
}
//================================================
ok,filesystem就说到这里吧,下一讲我们来使用filesystem里面提供的操作来实现一些有用的小工具,比如文件查找,目录拷贝等等。