15.file文件夹分析

这个文件夹里只有一个file.c文件,主要是获取文件系统中的目录信息以及文件信息,用到的一些库函数在以下博客中

Linux目录操作函数 scandir
目录项dirent详解及示例

先分析file.h中的结构体

 typedef struct FileMap {
 	char strFileName[256];  		/* 文件名 */
 	// int iFd; 
 	FILE * tFp;            			 /* 文件句柄 */
 	int iFileSize;         			 /* 文件大小 */
 	unsigned char *pucFileMapMem; 	 /* 使用mmap函数映射文件得到的内存 */
 }T_FileMap, *PT_FileMap;

/* 文件类别 */
typedef enum {
	FILETYPE_DIR = 0,  		/* 目录 */
	FILETYPE_FILE,     		/* 文件 */
}E_FileType;

/* 目录里的内容(文件、目录),名字和类型 */
typedef struct DirContent {
	char strName[256];     		/* 名字 */
	E_FileType eFileType;  		/* 类别 */	
}T_DirContent, *PT_DirContent;

file.c中的函数

/* 传入一个文件结构体	
 * 从这个结构体中拿到文件的名字
 * 根据名字打开后获得文件句柄,并赋值给结构体里对应成员
 * 获取文件描述符,再用stat获得这个文件信息,把大小赋值给对应的成员
 * 把映射到内存上
 */
int MapFile(PT_FileMap ptFileMap)	
{
	int iFd;
    FILE *tFp;
	struct stat tStat;
	
	/* 打开文件 */
	tFp = fopen(ptFileMap->strFileName, "r+");//用C库提供的打开
	if (tFp == NULL)
	{
		DBG_PRINTF("can't open %s\n", ptFileMap->strFileName);
		return -1;
	}
	ptFileMap->tFp = tFp;					//文件句柄赋值给结构体
    iFd = fileno(tFp);						//获得文件描述符 就是那个fd=open(...)的fd

	fstat(iFd, &tStat);						//获取文件的信息
	ptFileMap->iFileSize = tStat.st_size;	//把大小放到结构体中
	ptFileMap->pucFileMapMem = (unsigned char *)mmap(NULL , tStat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, iFd, 0);
	if (ptFileMap->pucFileMapMem == (unsigned char *)-1)
	{
		DBG_PRINTF("mmap error!\n");
		return -1;
	}
	return 0;
}
 /* 判断是不是一个目录,是返回1,不是返回0 */
static int isDir(char *strFilePath, char *strFileName)
{
    char strTmp[256];
    struct stat tStat;

    snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);	//路径下的名字连接成一个路径,指向这个文件(目录)
    strTmp[255] = '\0';

    if ((stat(strTmp, &tStat) == 0) && S_ISDIR(tStat.st_mode))	//首先根据地址获得文件信息,再判断是否是DIR
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
static int isRegDir(char *strDirPath, char *strSubDirName)	
{
    static const char *astrSpecailDirs[] = {"sbin", "bin", "usr", "lib", "proc", "tmp", "dev", "sys", NULL};
    int i = 0;								/* 这里最好还是设置初值 */
    
    /* 如果目录名含有"astrSpecailDirs"中的任意一个, 则返回0 */
    if (0 == strcmp(strDirPath, "/"))		/* 目录不是根目录的话,肯定不是特殊目录 */
    {
        while (astrSpecailDirs[i])			/* 一个一个地比 */
        {
            if (0 == strcmp(strSubDirName, astrSpecailDirs[i]))
                return 0;
            i++;
        }
    }
    return 1;    							/* 是一个常规目录 */
}
static int isRegFile(char *strFilePath, char *strFileName)	
{
    char strTmp[256];
    struct stat tStat;

    snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);
    strTmp[255] = '\0';

    if ((stat(strTmp, &tStat) == 0) && S_ISREG(tStat.st_mode)) /* 只有这个宏变了,宏的详解看C语言专栏 */
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
/* 这个函数传入一个目录的地址
 * 把这个目录中的所有目录文件以及常规文件的dirent结构体找出来,给它们都分配一个DirContent结构体
 * DirContent结构体只放着文件的名字和类别,把这个往上一级传出去
 * piNumber是标识共有多少个目录文件和常规文件
 */
/* 这里传 PT_DirContent **pptDirContents,算是一个三级指针
 * 因为要从这里拿走一个二重指针,所以这里要传一个三级指针
 */
int GetDirContents(char *strDirName, PT_DirContent **pptDirContents, int *piNumber)	
{
    PT_DirContent *aptDirContents;			//二重指针,指向一个指针数组,指针数组中,一个指针指向一个这种结构体
	struct dirent **aptNameList;			//二重指针,指向一个指针数组
	int iNumber;
	int i;
	int j;

	/* scandir函数的使用看博客
	 * 这里传入dirent的一个三级指针,需要传出一个dirent的二级指针,指向一个一级指针数组
	 * 这个一级指针数组里指向一个个struct dirent的结构体,一个结构体代表着一个文件的信息
	 * alphasort意思是按字母的顺序排序
	 * 返回值iNumber是二级指针指向的一级指针数组里的元素个数,即共有多少个文件
	 */
	iNumber = scandir(strDirName, &aptNameList, 0, alphasort);
	if (iNumber < 0)
	{
		DBG_PRINTF("scandir error : %s!\n", strDirName);
		return -1;
	}

	/* 忽略".", ".."这两个目录 */
	/* 分配iNumber个结构体空间,iNumber是文件的个数,给上面传出的所有文件(目录也是文件)使用*/
	aptDirContents = malloc(sizeof(PT_DirContent) * (iNumber - 2));	
	if (NULL == aptDirContents)
	{
		DBG_PRINTF("malloc error!\n");
		return -1;
	}
	/* aptDirContents这个是个二重指针,指向一个一重指针的数组
	 * 一重指针的数组是指向一个个的结构体,而这个结构体就是一个文件的信息——名字和类型
	 * 虽然aptNameList这个里面包含了名字信息,但是没有类型信息,所以本工程中重新创建了一个DirContent结构体
	 * pptDirContents是个三重指针,其指向一个二重指针,所以要把三重指针指向的值等于二重指针,如下
	 */
    *pptDirContents = aptDirContents;	

	for (i = 0; i < iNumber - 2; i++)
	{
		aptDirContents[i] = malloc(sizeof(T_DirContent));
		if (NULL == aptDirContents)
		{
			DBG_PRINTF("malloc error!\n");
			return -1;
		}
	}

	/* 先把目录挑出来存入aptDirContents */
	for (i = 0, j = 0; i < iNumber; i++)
	{
		/* aptNameList是一个二重指针,所以aptNameList[i]就是一个一级指针,用法如下
	     * 这里是要忽略掉.和..两个目录
	     */
		if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
			/*这里可以不释放空间,因为下面有集中释放的*/
			continue;
        /* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type,有的dirent结构体里面没有d_type成员*/
		/* if (aptNameList[i]->d_type == DT_DIR) */
		/* isDir是上面写好的函数,第一个传一个绝对目录,第二个传文件名,isDir函数会组合成一个绝对路径
		 * 然后调用stat函数来判断是不是一个目录的
		 */
        if (isDir(strDirName, aptNameList[i]->d_name))	//如果是目录的话s
		{
			strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);  //就把名字放到自己的那个结构体下面
			aptDirContents[j]->strName[255] = '\0';
			aptDirContents[j]->eFileType    = FILETYPE_DIR;
            free(aptNameList[i]);				//用完了要释放掉struct dirent结构体,这个在scandir中malloc的
            aptNameList[i] = NULL;				//防止野指针
			j++;
		}
	}

	/* 再把常规文件挑出来存入aptDirContents */
	for (i = 0; i < iNumber; i++)
	{
        if (aptNameList[i] == NULL)
            continue;
        
		/* 忽略".",".."这两个目录 */
		if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
			continue;
        /* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
		/* if (aptNameList[i]->d_type == DT_REG) */
        if (isRegFile(strDirName, aptNameList[i]->d_name))
		{
			strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
			aptDirContents[j]->strName[255] = '\0';
			aptDirContents[j]->eFileType    = FILETYPE_FILE;
            free(aptNameList[i]);
            aptNameList[i] = NULL;
			j++;
		}
	}

	/* 这里集中释放aptDirContents中未使用的项 */
	/* 之前为每一个struct dirent结构体都分配了一个struct DirContent
	 * 但是由于有.和..以及一些其他的非常规文件,所以之前分配的dirent结构体根本用不完
	 * 这里释放掉
	 */
	for (i = j; i < iNumber - 2; i++)	
	{
		free(aptDirContents[i]);
	}

	/* 非目录与常规文件的文件,未经历以上的过程,所以都没有释放空间
	 * 在这里从头到尾释放一遍
	 s*/
	for (i = 0; i < iNumber; i++)
	{
        if (aptNameList[i])
        {
    		free(aptNameList[i]);
        }
	}

	/* 最后用完了要释放掉这个指针指向的指针数组空间
	 * 数组空间的大小是 (一个指针占的空间)4*个数(struct dirent的个数)
	 * 而aptNameList本身也是有空间的,4字节,但是它是这个函数的局部变量,在栈里,这个函数结束后自动释放了
	 */
	free(aptNameList);	
	*piNumber = j;		//目录+常规文件的个数
	
	return 0;
}
 /* 这个函数的本质就是从一个目录strDirName中,最多取出iFileCountTotal个文件的绝对路径(包含文件名),放到apstrFileNames里
  * strDirName				:从这个目录中取出文件的路径加名字
  * piStartNumberToRecord	:从strDirName目录中开始遍历,前piStartNumberToRecord-1个文件不记录,从第piStartNumberToRecord文件开始记录
  * piCurFileNumber			:从strDirName目录遍历开始,当前弄到第几个文件了,包括递归执行子目录时,也是接着上层目录的数
  * piFileCountHaveGet		:已经记录了多少个文件了。如果piStartNumberToRecord一开始为0的话,那么这个值和piCurFileNumber是一样的
  * iFileCountTotal			:需要从这个目录中最多获得多少个文件的路径加名字,当获取够这么多时就返回,若遍历完整个目录包括子目录,
  *                           还是不够这个数,那么就返回,真正存了多少个文件名字,在piFileCountHaveGet里面
  * apstrFileNames			:一个字符串的数组,每一个字符串(一维字符数组)用来存放文件的名字信息
  *	注意!!!!由于下面会递归调用这个函数,以遍历整个目录及其子目录的文件
  *	所以这里的参数除了iFileCountTotal,其他的都是传入传出参数,其中,piStartNumberToRecord我认为是没有必要作为传入传出的,单独传入就好
  *	因为只要遍历到第piStartNumberToRecord个文件,之后的piCurFileNumber都不会小于piStartNumberToRecord这个值
  */
int GetFilesIndir(char *strDirName, int *piStartNumberToRecord, int *piCurFileNumber, int *piFileCountHaveGet, int iFileCountTotal, char apstrFileNames[][256])
{
    int iError;
    PT_DirContent *aptDirContents;  /* T_DirContent数组:存有strDirName目录下所有"文件"的名字和类型 */
    int iDirContentsNumber;         /* aptDirContents数组有多少项 */
    int i;
    char strSubDirName[256];

    /* 为避免访问的目录互相嵌套, 设置能访问的目录深度为10 */
#define MAX_DIR_DEEPNESS 10   
	/* 这个代表着第几层目录,0代表还没有在任何目录,1是输入的顶层目录,每进入一个子目录,就加一,退出一个子目录减一*/
    static int iDirDeepness = 0;	

    if (iDirDeepness > MAX_DIR_DEEPNESS)
    {
        return -1;
    }

    iDirDeepness++;    

	/* 获得这个目录下的所有文件的名字和类型,放到aptDirContents[i]指向的结构体里 */
    iError = GetDirContents(strDirName, &aptDirContents, &iDirContentsNumber);    
    if (iError)
    {
        DBG_PRINTF("GetDirContents error!\n");
        iDirDeepness--;
        return -1;
    }

    /* 遍历当前目录 */
    for (i = 0; i < iDirContentsNumber; i++)
    {
        if (aptDirContents[i]->eFileType == FILETYPE_FILE)	//如果是文件的话
        {
            if (*piCurFileNumber >= *piStartNumberToRecord)	//从第piStartNumberToRecord个文件开始取
            {
                snprintf(apstrFileNames[*piFileCountHaveGet], 256, "%s/%s", strDirName, aptDirContents[i]->strName);
                (*piFileCountHaveGet)++;					//已经搞到多少个文件数加一
                (*piCurFileNumber)++;						//当前文件是第几个文件
                /* 其实这一句没有必要,第一次执行到这里时,*piCurFileNumber == *piStartNumberToRecord了 
				 * 而之后每遍历一个文件,*piCurFileNumber值都会增加,不会再有小于*piStartNumberToRecord的时候了
				 */
                (*piStartNumberToRecord)++;					
                if (*piFileCountHaveGet >= iFileCountTotal)	//当前搞到的文件数 大于 要收录的文件数 时
                {
                    FreeDirContents(aptDirContents, iDirContentsNumber);	//就返回上一层,并且释放掉GetDirContents中malloc的空间
                    iDirDeepness--;							
                    return 0;
                }
            }
            else
            {
                (*piCurFileNumber)++;
            }
        }
    }

    /* 递归处理目录 */
    for (i = 0; i < iDirContentsNumber; i++)
    {
        if ((aptDirContents[i]->eFileType == FILETYPE_DIR) && isRegDir(strDirName, aptDirContents[i]->strName))
        {
			/* strSubDirName = 当前目录加上下一级目录 */
            snprintf(strSubDirName, 256, "%s/%s", strDirName, aptDirContents[i]->strName);
			/* 递归遍历子目录,把每一个子目录又当成顶层目录来处理,直到遍历完成*/
            GetFilesIndir(strSubDirName, piStartNumberToRecord, piCurFileNumber, piFileCountHaveGet, iFileCountTotal, apstrFileNames);
            if (*piFileCountHaveGet >= iFileCountTotal)
            {
                FreeDirContents(aptDirContents, iDirContentsNumber);	//用完后释放掉
                iDirDeepness--;
                return 0;
            }
        }
    }

    FreeDirContents(aptDirContents, iDirContentsNumber);
    iDirDeepness--;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值