文件信息显示
文件信息显示
1. 实现方法简介
首先,分析题干给出的要求:通过命令行程序,显示一个文件夹中所有的子文件夹和文件信息。因此,实现该程序的时候不仅要通过递归的方式寻找出所有的文件夹,还要能够通过命令行参数来设置根文件夹的路径。
在实现算法之前,首先确定语言,根据我的熟悉程度,我首先用Python试水,然后使用C++实现递归显示。
根据核心代码展示顺序,我先对C++实现算法进行简介。
通过搜集资料,首先我认识到,命令行参数的设置和int main(int argc, char* argv[])中的参数密切相关,argc 代表命令行中参数的个数(包含可执行程序!!!),而argv则是存储各个命令行参数的列表。所以相对于python而言,C++调用命令行参数要方便得多。在我的命令行设计中,格式为:XXX.exe ROOT_PATH,共计两个参数,所以argc=2,而实现显示文件信息所必要的参数root_path,则通过argv[1]即可传递。
设计函数为两个,一是寻找根目录下所有文件,并将绝对路径保存到列表files中的函数:
void Find_AllFiles(std::string root_path, std::vector<std::string>& files)
第二是接受命令行参数,并显示子文件夹及文件信息的函数:
void Reveal_File_Info(int argc, char* argv[])
函数Find_AllFiles通过vector容器实现对文件列表的信息扩展及添加。
对于文件的查找,可以通过io.h中的 _findfirst、 _findnext、 _findclose三个函数配合查找,找出文件的句柄(是一个顺序号,是识别打开文件的唯一识别证据)并且把文件信息保存到 _finddata_t型的结构体fileinfo中
_finddata_t型的结构体在宏定义中被定义为 _finddata64i32_t
struct _finddata64i32_t
{
unsigned attrib;
__time64_t time_create; // -1 for FAT file systems
__time64_t time_access; // -1 for FAT file systems
__time64_t time_write;
_fsize_t size;
char name[260];
};
因此可以通过该结构体中的元素得到该文件的各个属性值。而attrib尤其方便,它通过数值能表示:
#define _A_NORMAL 0x00 // Normal file - No read/write restrictions
#define _A_RDONLY 0x01 // Read only file
#define _A_HIDDEN 0x02 // Hidden file
#define _A_SYSTEM 0x04 // System file
#define _A_SUBDIR 0x10 // Subdirectory
#define _A_ARCH 0x20 // Archive file
所以可以通过attrib来判断是否是子文件,从而实现递归起始条件的判断。
而第二个函数Reveal_File_Info则是主要通过对结构体fileinfo的操作,显示文件的属性。
在Python实现中,由于有两个好用的包,实现简单了很多。在对于文件的查找中,可以通过os.walk方法,但是os.path.walk被pathlib中的Path替代(因为pathlib中的路径和文件通过面向对象的方法实现,封装更好)通过pathlib.Path方法可以直接通过root_path直接得到,并且通过stat()方法得到属性。
但是对于python,命令行不是很方便实现,通过click包,可以将命令行参数绑定到某些函数中,如command()函数,借用别的函数的接口来是按对于命令行参数的获取。
2. 核心代码
C++实现
void Find_AllFiles(std::string root_path, std::vector<std::string>& files) {
intptr_t hFile = 0;//文件句柄
struct _finddata_t fileinfo;//文件信息
std::string p;
if ((hFile = _findfirst(p.assign(root_path).append("\\*").c_str(), &fileinfo)) != -1)
{
do {
if ((fileinfo.attrib & _A_SUBDIR)) { //比较文件类型是否是文件夹
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
files.push_back(p.assign(root_path).append("\\").append(fileinfo.name));
std::cout << "子文件夹:" << p << std::endl;
//递归搜索
Find_AllFiles(p.assign(root_path).append("\\").append(fileinfo.name), files);
}
}
else {
files.push_back(p.assign(root_path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0); //寻找下一个,成功返回0,否则-1
_findclose(hFile);
}
}
void Reveal_File_Info(int argc, char* argv[]) {
// argc == 2
// argv[1] == root_path
intptr_t hFile = 0;//文件句柄
std::vector<std::string> files_list;
if (argc != 2) {
std::cout << "please type the root path" << std::endl;
}
else {
Find_AllFiles(argv[1], files_list);
std::string p;
struct _finddata_t fileinfo;//文件信息
for (unsigned int i = 0; i < files_list.size(); ++i) {
hFile = _findfirst(p.assign(files_list[i]).c_str(), &fileinfo);
std::cout << std::endl;
std::cout << "绝对路径:" << files_list[i] << std::endl;
std::cout << "文件属性:" << fileinfo.attrib << std::endl;
std::cout << "文件名称:" << fileinfo.name << std::endl;
std::cout << "文件大小:" << fileinfo.size << std::endl;
std::cout << "文件最后一次被访问的时间:" << fileinfo.time_access << std::endl;
std::cout << "创建时间:" << fileinfo.time_create << std::endl;
std::cout << "文件最后一次被修改的时间:" << fileinfo.time_write << std::endl;
std::cout << std::endl;
}
}
}
Python实现
import pathlib
import click
@click.command()
@click.option('--path', 'root_path', prompt='Directory Path', default=1, help='The Path of Directory you wanna reveal.', type=str)
def Reveal_File_Info(root_path):
root_dir = pathlib.Path(root_path)
for file_path in root_dir.rglob('*'):
click.echo()
click.echo("绝对路径: " + str(file_path))
click.echo("文件名: " + file_path.name)
click.echo("扩展名: " + file_path.suffix)
click.echo("inode保护模式: " + str(file_path.stat().st_mode))
click.echo("inode节点号: " + str(file_path.stat().st_ino))
click.echo("inode驻留的设备: " + str(file_path.stat().st_dev))
click.echo("inode的链接数: " + str(file_path.stat().st_nlink))
click.echo("所有者的用户ID: " + str(file_path.stat().st_uid))
click.echo("所有者的组ID: " + str(file_path.stat().st_gid))
click.echo("普通文件的大小: " + str(file_path.stat().st_size) + " 字节")
click.echo("上次访问的时间: " + str(file_path.stat().st_atime))
click.echo("最后一次修改的时间: " + str(file_path.stat().st_mtime))
click.echo("最新的元数据更改的时间(如Unix)/创建时间(如Windows): " + str(file_path.stat().st_ctime))
click.echo()
结果

3. PSP表格
| PSP各阶段 | 预估耗时(分钟) | 实际耗时(分钟) | |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 明确需求和其他因素,估计以下各个任务需要多少时间 | 5 | 5 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术、新工具的时间) | 20 | 60 |
| · Design Spec | · 生成设计文档(整体框架的设计,各模块的接口) | 5 | 2 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 5 | 0 |
| · Coding Standard | · 代码规范 (为目前的开发制定或选择合适的规范) | 5 | 1 |
| · Design | · 具体设计(用流程图、伪代码等方法来设计具体模块) | 5 | 1 |
| · Coding | · 具体编码 | 30 | 60 |
| · Code Review | · 代码复审 | 10 | 10 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 40 | 85 |
| Reporting | 报告 | ||
| · Test Report | · 测试报告(发现了多少bug,修复了多少) | 10 | 8 |
| · Size Measurement | · 计算工作量(多少行代码,多少次签入,多少测试用例,其他工作量) | 5 | 2 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划(包括写文档、博客的时间) | 30 | 40 |
| 合计 | 170 | 274 |

浙公网安备 33010602011771号