实验二 目录树的遍历

实验二 目录树的遍历

背景

正确理解程序4-22。程序4-22递归降序遍历目录层次结构,并按照文件类型进行计数。主要涉及到三个函数myftw()、dopath()和myfunc()。myftw ()函数以所带参数pathname为要遍历的起始目录,计算出该目录下各种不同类型的文件的个数和所占百分比,并显示出来。它调用了另外两个函数,一个是dopath()函数,这是一个递归函数,对指定的起始目录下的每个目录项,按深度优先进行遍历;而对所访问的节点,则调用myfunc()进行处理。main函数输出统计结果。三个函数的参数含义如下:

(1) static int myftw(char *pathname, Myfunc *func);

pathname给出指要遍历开始的目录。

func是Myfunc类型的函数指针,定义访问的实际操作。

(2) static int dopath(Myfunc *func);

pathname给出指要遍历开始的目录。

(3) static int myfunc(const char *pathname, const struct stat *statptr, int type);

pathname指向当前访问节点的路径名。

statptr指向当前访问节点的i-节点的结构,该结构保存有许多该文件的信息。

type给出当前访问节点的类型,在实验中可以自己定义它的含义。

myfunc()的返回值通常是0,实际上在程序4-22中它的值总是0。但是在dopath()函数中,myfunc()的返回值非0意味着终止遍历。

另外,程序4-22中用到的函数path_alloc()用于分配存放路径名的内存空间。

实验内容和实现思路

实验二要求根据用户输入的命令行选项的不同,来实现三种功能:

一、

(1) argc为2时,命令格式为

​ myfind

它除了实现程序4-22原本的功能外,还要统计出,在常规文件中,文件长度不大于4096字节的常规文件,在所有允许访问的普通文件中所占的百分比。程序也不允许打印出任何路径名。这个功能实现比较简单,只要略加修改myfunc()和main()这两个函数就可以了。

实现思路:这一问比较简单,只要再myfunc函数中增加对文件大小的判断即可,这里使用参数里的Stat结构体里的Statptr->st_size(文件的大小)来跟4096比,不大于则统计量加1,其它文件格式也做相应的处理。修改后的myfunc函数如下:

static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
	switch(type)
	{
	case FTW_F:
		switch( statptr->st_mode & S_IFMT )
		{
		case S_IFREG: 
        //实现2-(1)功能 统计出,在常规文件中,文件长度不大于4096字节的常规文件
            if(statptr->st_size<4096) size++;break;
			nreg++; 
			printf("reg: %s\n", fullpath); 
			break;
		case S_IFBLK: 
			nblk++; 
			printf("blk: %s\n", fullpath); 
			break;
		case S_IFCHR: 
			nchr++; 
			printf("chr: %s\n", fullpath);
			break;
		case S_IFIFO: 
			nfifo++; 
			printf("fifo: %s\n", fullpath);
			break;
		case S_IFLNK: 
			nslink++; 
			printf("slink: %s\n", fullpath);
			break;
		case S_IFSOCK: 
			nsock++; 
			printf("socket: %s\n", fullpath);
			break;
		case S_IFDIR: 
			fprintf(stderr, "For S_IFDIR for %s\n", pathname);
			exit(1);
		}
		//书中没有输出遍历的结果,这个是我自己加上去的
		break;
	case FTW_D:
		ndir++; 
		printf("DIR: %s\n", fullpath);
		break;
	case FTW_DNR:
		fprintf(stderr, "can't read directory %s\n", pathname);
		break;
	case FTW_NS:
		fprintf(stderr, "stat error for %s\n", pathname);
		break;
	default:
		fprintf(stderr, "unkown type %d for pathname %s\n", 
			type, pathname);
	}
	return 0;
}

执行./work2 /etc 和 ./work2 ./后的结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lrx3PQsh-1603527306066)(/home/linli/.config/Typora/typora-user-images/image-20201010234540423.png)]

二、

(2) argc为4且argv[2] == “-comp”时,命令格式为

​ myfind -comp

它的功能是输出在目录子树之下,所有与文件内容一致的文件的绝对路径名。不允许输出任何其它的路径名,包括不可访问的路径名。为提高程序效率,在比较文件是否相同时,可先比较两个文件的大小,如果大小不同,则内容肯定不同,这样就免去了读文件所浪费的时间;如果大小相同,则再通过读文件进行比较。此时应当注意输入缓冲区不必开的太大,你可以从实验一得到启发。由于要求输出符合要求的文件的绝对路径名,因此当参数pathname不是绝对路径时,要调用getcwd()等函数来取得文件的绝对路径名。

实现思路:用一个函数myfunc2(const char * pathname,const struct stat *statptr,int type)来实现该功能。首先用全局变量dsize和dbuf保存待比较文件的大小和内容,对传进来的参数pathname进行判断它不是目录,否则不做处理。用文件pathname的i节点statptr->st_size和length比较,不等则两文件内容肯定不会相同。大小相等则对两文件的内容进行比较,开辟一个大小为dsize的缓冲区buf,将pathname的文件写入buf,然后用strcmp进行比较。这里有一个问题,绝对路径需要转化成相对路径,myfunc2()函数这里我调用<stdlib.h>里的realpath函数,很方便将绝对路径转化为相对路径。而在下一个函数myfunc3里,我将用另外一种方法进行转换。

myfunc2函数如下:

static int myfunc2(const char *pathname, const struct stat *statptr, int type)
{
  
  int fd,len;
  char *buf,*fullpathname;  //存放文件内容和文件名称,用于和dbuf比较内容
//   printf("pathname = %s\n",pathname);
  buf=(char *)malloc(sizeof(char)*dsize);
  fullpathname=malloc(len);
  if(type==FTW_F)
  {
    if(dsize==statptr->st_size)   //首先比较大小,如果大小不同,就不需要再比较
    {
      if((fd=open(pathname,O_RDONLY))==-1)
        printf("cant't openfile\n");
      read(fd,buf,dsize);
      //文件内容相同
      if(strcmp(dbuf,buf)==0)
      {
        //获取文件的绝对路径名
        // getcwd(fullpathname,len);
        count++;
        char actualPath[100] = {0};
        char* ptrRet = NULL;
        ptrRet =  realpath(pathname,actualPath);
        printf("%s\n",actualPath);
      }
      close(fd);
    }
  }
  return(0);
}

运行./work2 /home/linli -comp work2.c和./work2 …/…/ -comp work2.c的结果如下:

在这里插入图片描述

三、

(3) argc 大于等于4且argv[2] == “-name”时,命令格式为

​ myfind -name …

…是一个以空格分隔的文件名序列(不带目录)。命令输出在目录子树之下,所有与…序列中文件名相同的文件的绝对路径名。不允许输出不可访问的或无关的路径名。实现方法可以通过循环,把当前遍历的文件名和这个序列中的文件名进行比较,如果和序列中的一个文件名相同,就符合条件,此时输出符合条件的文件的绝对路径名。

实现思路:对于以空格分隔的文件名序列,我们在main函数里用for循环遍历读取,每当读取一次就做一次递归遍历操作,这样时间复杂度有点高,另一种想法是事先将文件名序列存放在一个vector里,递归遍历目录树的时候一起比较。myfunc2的实现思路和myfunc2类似,这里是比较文件名序列和目录树递归遍历的文件名是否相同,只要比较pathname(当前访问节点的路径名的最后部分)和文件名序列就可(即从最后一位开始比较)。这里我再尝试了另一种将相对路径转化为绝对路径的方法,就是先用chdir转变当前工作目录,然后用getcwd获取当前工作目录的绝对路径,这里的转化需要对pathname做一些处理,具体见myfunc3函数:

static int myfunc3(const char *pathname, const struct stat *statptr, int type)
{
    int len=80,filenamelen=0,pathnamelen=0;
    char *fullpathname;
    if(type ==FTW_F){
            //获取文件名长度
        filenamelen = strlen(filename);
        // printf("filenamelen = %d\n",filenamelen);
        // printf("filename = %s",filename);
        pathnamelen=strlen(pathname);
        // printf("pathnamelen = %d\n",pathnamelen);
        // printf("pathname = %s\n",pathname);
        fullpathname=malloc(len+100);

        for(int i=1;i<filenamelen;i++){
            if(pathname[pathnamelen-i]!=filename[filenamelen-i]){
                return 0;
            }
        }
        if(pathname[pathnamelen-filenamelen-1]=='/'){
            //获取文件的绝对路径名
            // printf("pathname:%s\n",pathname);
            int pos;
            for(int i=pathnamelen-1;i>=0;i--){
                if(pathname[i]=='/') {pos = i;break;}
            }
            // printf("pathnamelen:%d\n",pathnamelen);
            // printf("pos = %d\n",pos);
            char *path = (char*)malloc(sizeof(char)*pos);
            strncpy(path,pathname,pos);
            if(chdir(path) <0) {
                printf("failed\n");
            }
            getcwd(fullpathname,len);
            printf("%s/%s\n",fullpathname,filename);
            count++;
        }
    }
    return 0;
}

执行./work2 /home/linli/CLionProjects/ -name main.cpp 后的结果如下:
在这里插入图片描述

执行./work2 …/…/ -name work2.c 结果如下

在这里插入图片描述

实验心得

1、熟悉了文件IO函数的调用,熟悉了UNIX系统的文件系统,对UNIX系统有了更深的认识

2、练习编写makefile,增强了代码编写的能力。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目的掌握文件目录有关的系统调用库函数要求: 1、编写程序myfind 命令语法: myfind <pathname> [-comp <filename> | -name <str>…] 命令语义: (1)myfind <pathname> 的功能: 除了具有与程序4-7相同的功能外,还要输出在<pathname>目录之下,文件长度不大于4096字节的常规文件,在所有允许访问的普通文件中所占的百分比。程序不允许打印出任何路径名。 (2)myfind <pathname> -comp <filename>的功能: <filename>是常规文件的路径名(非目录名,但是其路径可以包含目录)。命令仅仅输出在<pathname>目录之下,所有与<filename>文件内容一致的文件的绝对路径名。不允许输出任何其它的路径名,包括不可访问的路径名。 (3)myfind <pathname> -name <str>…的功能: <str>…是一个以空格分隔的文件名序列(不带路径)。命令输出在<pathname>目录之下,所有与<str>…序列中文件名相同的文件的绝对路径名。不允许输出不可访问的或无关的路径名。 <pathname>和<filename>均既可以是绝对路径名,也可以是相对路径名。<pathname>既可以是目录,也可以是文件,此时,目录为当前工作目录。 2、注意尽可能地提高程序的效率。注意避免因打开太多文件而产生的错误。 3、遍历目录时,访问结点(目录项)的具体操作应当由遍历函数dopath携带的函数指针参数决定。这样程序的结构清晰,可扩充性好。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值