300 likes | 505 Views
Project2: 文件夹同步工具. 任务 背景. 有 2 个文件夹 A 和 B ,经过之前的同步操作,文件夹 B 中的内容与文件夹 A 中内容完全一致,现在用户对 A 文件夹中部分文件进行了编辑、移动、删除或者新增的操作,请你的程序根据文件夹 A 中的内容变化对文件夹 B 进行增量更新。. 功能要求. 文件夹 A 中保持不变的文件在文件夹 B 中保持不变,不更新。 给出文件夹 A 中文件的更新情况列表,即列出与上次同步后文件夹 A 中所有发生变化的文件列表, 包括 a ) 与上次更新相比,所有新增文 件的列表 b ) 与上次更新相比,所有删除文 件的列表
E N D
任务背景 • 有2个文件夹A和B,经过之前的同步操作,文件夹B中的内容与文件夹A中内容完全一致,现在用户对A文件夹中部分文件进行了编辑、移动、删除或者新增的操作,请你的程序根据文件夹A中的内容变化对文件夹B进行增量更新。
功能要求 • 文件夹A中保持不变的文件在文件夹B中保持不变,不更新。 • 给出文件夹A中文件的更新情况列表,即列出与上次同步后文件夹A中所有发生变化的文件列表,包括 • a) 与上次更新相比,所有新增文件的列表 • b) 与上次更新相比,所有删除文件的列表 • c) 与上次更新相比,所有发生变动的文件的列表 • 只对文件夹A中发生变化的文件在文件夹B中更新,即针对上述3中变化的文件,删除文件夹B中的相应原文件,并将A中最新文件复制到文件夹B中。 • 注意:删除时需要提示用户是否确定删除源文件。
其他要求 • 简单易用、界面友好 • 足够的提示信息 • 输入错误的处理 • 更新速度快 满分:85分
提高要求 • 将上述工具移植到windows平台下。 • 在文件夹B中保留更新前的文件(比如,重新以特定的命名方式命名或者保存在特定的文件夹中的方式)。 • 开发图形界面 满分:100
解题思路 • 简单想法: • 遍历文件夹A、B,对于A中的任一文件f1, 在B中查找与f1同名文件f2 • 如果找到f2,读取f1和f2的文件信息是否一致 • 一致:表示文件没有发生变化,什么也不做 • 不一致:用f1覆盖f2 • 如果没有找到,表示该文件是新文件,则将其复制到文件夹B中 • 还有没有其他问题?
实现细节 • 如何遍历整个文件夹? • Linux命令 • ls –R • Find • 调用操作系统提供API(应用编程接口) • opendir()、readdir()、closedir() • 如何读取文件信息? • Linux相关命令: • 系统函数:
问题1:遍历得到目录下所有的文件 • 方法1:利用linux命令的结果 • ls –R • find
find命令 • find是linux中最有用的命令之一 • 用于在一个目录(及子目录)中搜索文件 • 可以指定一些匹配条件,如按文件名、文件类型、用户甚至是时间戳查找文件 • 举例: • find . -name "*.c” • 在当前目录及其子目录中查找任何扩展名为c的文件 • find . -type f • 查找当前目录中的每一个普通文件 • find / -mtime -5 • 在系统根目录下查找更改时间在5日以内的文件
如何在程序中利用linux命令的结果 • system()函数:调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕 • 如何得到命令执行的结果 • system的返回值不能待会命令的执行结果 • 输入输出重定向 原型: intsystem(const char *command); 返回值: 如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。
输入输出重定向 • Linux重定向是指对原来系统命令的默认执行方式进行改变 • <和>实现输出输入的重定向 • 使用< 或 > 时,相当于使用 0< 或 1> • 简单举例 • cmd > file • 把cmd命令的输出重定向到文件file中。如果file已经存在,则清空原有文件,例如:ls-l>filenames.txt • cmd < file • 使cmd命令从file读入
Linux的管道 • ls-l|wc-l
方法1:实现提示 • 在你的程序中,利用system函数得到目录下的所有文件,并将结果保存到文件中 • system(“find.>filelist.txt”); • system(“ls -l –R > filelist1.txt”);
方法2:利用操作系统提供的API • 目录操作相关的API • opendir • 打开目录,并返回DIR*形态的目录流,接下来对目录的读取和搜索都要使用此返回值。 • readdir • 读取目录 • 返回一个代表目录里下一个项目的指针 • closedir • 关闭目录
opendir:打开目录 • 表头文件 • #include<sys/types.h> • #include<dirent.h> • 定义函数 • DIR * opendir(const char * name); • 函数说明 • opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,接下来对目录的读取和搜索都要使用此返回值。 • 返回值 • 成功则返回DIR* 型态的目录流,打开失败则返回NULL。
readdir函数 • 表头文件 • #include<sys/types.h> • #include<dirent.h> • 函数原型 • structdirent * readdir(DIR * dir); • 函数说明 • 返回一个代表目录 dirp里下一个项目的指向 dirent结构的指针。如果到达目录流结尾或出错它返回 NULL。 • 返回值 • 成功则返回下个目录进入点。有错误发生或读取到目录文件尾则返回NULL。
readdir函数(2) 在 Linux 系统里,dirent结构定义如下: structdirent { ino_td_ino; /* inode number */ off_td_off; /* 下一个项目的偏移 */ unsigned short d_reclen; /* 本记录的长度 */ unsigned char d_type; /* 文件类型;不是被所有文件系统支持 */ char d_name[256]; /* 文件名 */ };
closedir:关闭目录 • 表头文件 • #include<sys/types.h> • #include<dirent.h> • 定义函数 • intclosedir(DIR *dir); • 函数说明 • closedir()关闭参数dir所指的目录流。 • 返回值 • 关闭成功则返回0,失败返回-1,错误原因存于errno中。
编程提示 pDir=opendir(path) while( (ent=readdir(pDir))!=NULL ) { //dosomething; } closedir(pDir);
问题2:如何确定文件是否更新过? • Linux文件的时间信息 • 如何读取文件信息? • Linux相关命令:stat • 系统函数: • stat()、fstat()、lstat()
Linux中文件时间 • Windows下: • 创建时间 • 修改时间 • 访问时间 • 访问时间 • 修改时间 • 状态改变时间 • 该文件最后一次被修改的时间 • 通过chmod、chown命令修改一次文件属性,这个时间就会更新。
如何获取文件信息 • Linux相关命令:stat • 系统函数: • stat()、fstat()、lstat()
lstat函数 • 返回与文件有关的信息结构 • intlstat(const char *path, struct stat *buf); structstat { dev_tst_dev; /* 文件所在设备的标识 */ ino_tst_ino; /* 文件结点号 */ mode_tst_mode; /* 文件保护模式 */ nlink_tst_nlink; /* 硬连接数 */ uid_tst_uid; /* 文件用户标识 */ gid_tst_gid; /* 文件用户组标识 */ dev_tst_rdev; /* 文件所表示的特殊设备文件的设备标识 */ off_tst_size; /* 总大小,字节为单位 */ blksize_tst_blksize; /* 文件系统的块大小 */ blkcnt_tst_blocks; /* 分配给文件的块的数量,512字节为单元 */ time_tst_atime; /* 最后访问时间 */ time_tst_mtime; /* 最后修改时间 */ time_tst_ctime; /* 最后状态改变时间 */ };
代码分析:遍历目录 #include <unistd.h> #include <stdio.h> #include <dirent.h> #include <string.h> #include <sys/stat.h> void List( char *path, int indent ) { structdirent* ent = NULL; DIR *pDir; char dir[512]; struct stat statbuf; if( (pDir=opendir(path))==NULL ) { fprintf( stderr, "Cannot open directory:%s\n", path ); return; }
while( (ent=readdir(pDir))!=NULL ) { //得到读取文件的绝对路径名 snprintf( dir, 512,"%s/%s", path, ent->d_name ); //得到文件信息 lstat( dir, &statbuf); //判断是目录还是文件 if( S_ISDIR(statbuf.st_mode) ) { //排除当前目录和上级目录 if(strcmp( ".",ent->d_name) == 0 || strcmp( "..",ent->d_name) == 0) { continue; } //如果是子目录,递归调用函数本身,实现子目录中文件遍历 printf( "%*s子目录:%s/\n", indent, "", ent->d_name ); //递归调用,遍历子目录中文件 List( dir, indent+4 ); } else { printf( "%*s文件:%s\n", indent, "", ent->d_name ); } } closedir(pDir); }
int main(intargc, char* argv[]) { if(argc == 2) { List( argv[1], 2 ); } else { char *s = "."; List( s, 2 ); } return 0; }
其他问题 • 提高效率 • 可以将文件信息保存起来 • 移植到windows平台 • linux下的API换成windows平台的响应API