这个文件夹里只有一个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;
}