2.1 linux文件系统应用实例:递归遍历目录

  • 看过一遍视频,有了点思路之后写的
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

void read_file(char *file);//函数声明
void poll(char *file);

void sys_err(const char *buf)
{
    perror(buf);
    exit(0);
}

void poll(char *file)   //判断文件是否为目录文件,并且输出文件大小
{
    struct stat st;

    if(stat(file, &st) < 0) //获取文件inode信息
        sys_err("stat");

    if((st.st_mode & S_IFMT) == S_IFDIR)  //判断输入的文件名是目录还是其他类型的文件,如果是目录开始遍历目录

        read_file(file);//遍历目录的记录项
//else 
        printf("%8d\t%s\n", (int)st.st_size, file);//输出文件大小
    return ;
}

void read_file(char *file)  //遍历目录的记录项
{
    DIR *dir;   
    struct dirent *read;
    char buff[255] = "0";

    if((dir = opendir(file)) < 0)//获取文件的DIR指针
        sys_err("opendir");

    //当read==NULL的时候,说明该条目录已经遍历完毕
    while((read = readdir(dir)) != NULL) {
        if(strcmp(read->d_name,".") == 0 || strcmp(read->d_name, "..") == 0) //.和.. 一个指向当前目录,一个指向上一级目录,为了避免死循环,当记录项是.和..的时候跳过,继续读下一条记录项
            continue;
        else {
            sprintf(buff, "%s/%s", file, read->d_name); //通过人为的加上'/',使得poll函数可以访问下一级目录
            poll(buff);//判断本条记录项是目录还是文件
        }   
    }   
    closedir(dir);
    return;
}
int main(int argc, char *argv[])
{
    if(argc < 2) {
        printf("./a.out path\n");
        exit(0);
    }

    poll(argv[1]);

    return 0;
}
  • 注意事项:
  • 在设计函数接口的时候,要注意char型指针的级别,在poll函数的需要接收一个文件名,这肯定是一个char型的指针,因为char *argv[]本质上是一个2级指针,所以只要把argv[1]这个1级指针传过去就好

  • 因为接口都是用来传递字符串的,所以设置成char*类型就可以了

  • (st.st_mode & S_IFMT) == S_IFDIR)这条语句在“文件I/O函数练习及注意事项”博文里已经写到,这里就不在赘述
  • sprintf(buff, "%s/%s", file, read->d_name)这句话很重要,在添加’/’的时候一定要传给第三个字符串,不要传给file,如果给了file,在运行完本次的poll,在调用下一条记录项的时候,file里面的内容仍继续参与寻址,但此时file里面还存有上一条记录项的文件名;简单的一句换归纳,就是file无法被除第一条记录项之外的其他记录项用作寻址功能。(如果您看到本条,并有更好的说法欢迎指正)
  • 在linux里面,a/b和a//b同样适用,都可以寻找到文件而且中间的’/’的个数可以不为1,最大值不知道,但这个手动添加’/’提供了方便
  • 由于上述代码为本人书写,所以有很多细节没有考虑到,只是实现了大体的功能,下列代码考虑到了大部分细节,以供参考
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#define MAX_PATH 1024 //设定路径最大长度,防止死循环

void dirwalk(char *dir, void(*fcn)(char *)) //遍历目录文件的记录项
{
    char name[MAX_PATH];
    struct dirent *dp;
    DIR *dfd;

    if((dfd = opendir(dir)) == NULL) {  //打开dir的记录项指针
        fprintf(stderr, "dirwalk: can't open %s\n", dir);
    }   
    while((dp = readdir(dfd)) != NULL) {//读取记录项
        if(strcmp(dp->d_name,".") == 0 ||strcmp(dp->d_name, "..") == 0)
            continue;
        if(strlen(dir) + strlen(dp->d_name) + 2 > sizeof(name))如果,目录项和文件项的和再加上预留出来的'/'长度以及字符串末尾的'\0'大于name的长度,字符串就溢出了,就不能执行fcn函数了
            fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name);
        else {
            sprintf(name, "%s/%s", dir, dp->d_name);//人为加上地址符号
            (*fcn)(name);//读取文件大小并输出
        }   
    }   
    closedir(dfd);
}

void fsize(char *name)//读取文件大小并输出
{
    struct stat stbuf;

    if(stat(name, &stbuf) == -1) {
        fprintf(stderr, "fsize: can't access %s\n", name);
        return;
    }
    if((stbuf.st_mode & S_IFMT) == S_IFDIR) //判断是否是目录
        dirwalk(name, fsize);
    printf("%8ld %s\n", stbuf.st_size, name);//读取文件大小
}

int main(int argc, char **argv)
{
    if(argc == 1)   //如果没有接收到字符,就默认打开当前目录
        fsize(".");
    else
        while(--argc > 0)
            fsize(*++argv); // *++argv等同于argv[1]
    return 0;
}

你可能感兴趣的:(c,递归,Linux编程)