用递归的方法输出以命令行参数的形式给定的目录下的所有子目录。
整体思路:
参考树的遍历,每打开一个目录判断当前打开的是目录还是文件,是文件则终止,是目录则继续递归打开其子目录,直到给定目录下的所有目录均被输出为止,本文用到的是dfs序。
要解决的问题:
- 如何读取linux下的命令行参数
- 如何打开并读取某个目录
- 如何判断当前打开的是目录还是文件
- 如何输出已经打开的目录或者文件
解决方案
读取linux下的命令行参数方法:
int main(int argc,char* argv[])
- argc是命令行总的参数个数 (自动生成,不需要填写)
- **argv[]**是argc个参数,其中第0个参数是程序的全名,以后的参数是命令行后面跟的用户输入的参数。
- 使用方法:
vs下使用方法:工程–>属性–>配置属性–>调试->“命令参数”
本文采用的代码均在Linux命令行中实现,只需在执行可执行文件时在其后填写参数即可:
例如:
./File ..
//File是可执行文件名,(“..”)是给的参数,表示当前目录的父目录
如何打开并读取某个目录:
文件的打开和关闭,这里用到两个函数
opendir() //打开一个目录
closedir() //关闭一个目录
//原型分别为
DIR *opendir(const char *name);//返回DIR *类型的指针,稍后就能用到
int closedir(DIR *dir);
文件的读取用到一个函数
readdir();
//函数原型如下
struct dirent *readdir(DIR *dir);
通过对以上三个函数的原型的观察,不难发现opendir() 的返回类型正是readdir()需要的参数。所以我们在程序中定义一个 DIR *类型的变量来接受opendir() 然后再将其传给readdir()就可以完成对一个目录的打开并读取。
结构体struct dirent简介:
struct dirent*类型作为readdir()的返回值,再程序程序中需要多次访问(文件名,文件类型等),这里有必要对它更进一步了解
原型如下(定义在dirent.h)
#include <dirent.h>
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
其中我们需要用到的成员有:
- d_type:表示的是这个结构体所表示的文件的类型
- d_name:表示该文件的文件名,在输出时需要用到。
如何判断当前打开的是目录还是文件
readdir返回值中struct dirent.d_type的取值表示了不同的文件类型,Linux定义了以下宏用来表示文件类型,其中DT_DIR = 4表示打开的是目录。
enum {
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR //目录
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
如何输出已经打开的目录或者文件
通过以上判断,当前目录是文件的话就利用dirent结构体的成员d_name来输出文件名,如果是目录的话就递归的输出该目录的子目录
entry=readdir(d)
entry->d_name
最后,实现的代码如下:
#include<stdio.h>
#include <dirent.h>
#include <string.h>
int form=0;//用来控制格式
void PrintDir(char* a)
{
char base[1000];
struct dirent* entry;
DIR* d = opendir(a);//打开文件a并 将返回值赋给d
if (d == NULL)
printf("d == NULL");
while ( (entry=readdir(d)) != NULL)
{
if(strcmp(entry->d_name,".")==0 || strcmp(entry->d_name,"..")==0)
continue;//当前目录和父目录不再重复输出
printf("%d",entry->d_type);
for(int i=0;i<=form;i++)
printf("-");
printf("%s\n",entry->d_name);
if(entry->d_type==4)
{ form++;
strcpy(base,a);
strcat(base,"/");
strcat(base,entry->d_name);
PrintDir(base);
form--;
}
}
closedir(d);
}
int main(int argc,char** argv)
{
PrintDir(argv[1]);
return 0;
}
//输出当前工作目录
//char BasePath[1000];
//getcwd(BasePath, 999);
//rintf("当前工作目录是 : %s\n",BasePath);
//printf("%s",argv[0]);
//递归输出目录
运行截图:
部分代码的分析:
格式的控制:
- 适当的控制输出的格式可以让输出的结果更加直观。
这里设置一个全局变量form来记录当前文件的格式,然后输出文件名之前先输出相应数量的“-”即可。对于输出的文件,同级目录前有相同长度的格式字符串,格式字符串的长度反映了其相对于根目录的深度
相对路径与绝对路径:
- 由于每次由当前目录的子目录名得到的总是相对路径,而opendir()函数接受的应当是一个绝对路径,所以每次递归的参数都应该是由三部分组成,即:当前目录 + ‘/’ + 子目录名。这样组成的绝对路径作为参数进行递归才能进行下去。
命令行参数的读取与使用
- 由于本程序只需要用到一个命令行参数即给定的目录。所以只需要访问argv[1]即可(argv[0]里面存的是程序的名称)
直接把argv[0]作为初始参数传给PrintDir即可完成。