这段时间笔者在准备一个比赛,需要用C/C++实现一个将磁盘的文件目录树读入到内存中(其实就是将磁盘中的文件目录树读入到一个树形结构中),笔者是搞JAVA的,这次要用C实现这样一个功能,着实让笔者头痛了很久,不过硬着头皮也得上啊,于是笔者选择了用C实现。
这里的树形结构的节点,笔者选择了孩子-兄弟表示法,这样方便将普通树形结构转化成二叉树结构,这样操作二叉树的相关函数也可以用于操作普通的树形结构。
以下是相关代码,一共只有两个函数,一个用于判断当前节点是文件还是文件夹,一个用于将磁盘上的文件目录树读入到内存中。
代码中有这样几个问题希望读者们注意到:
1.fip = readdir(dp)
这段代码用来读取文件夹中的文件目录,每执行一次这段语句,fip指针都会顺次指向该目录下的文件,那么通过fip指针,我们可以判断其指向的文件是文件夹还是普通文件。
2.FileNode* rootNode, FileNode* preNode, FileNode* currentNode
以上几个变量分别是当前目录的根目录节点,记住是当前目录;fip指针上一次指向的节点;fip目录当前指向的节点。那么通过以上几个变量就能够有效地递归访问文件夹下的各个文件了。
3.sprintf_s(dirname, "%s\\%s", dir, currentNode->filename);
这里每一个node里存储的只是当前节点的文件名或者文件夹名,那么判断这个文件是否是文件夹,我们还是这个文件的绝对路径,也就是dir。
这里通过调试或者前序遍历就可以看到tree节点的内容了,这样某个根路径下的文件目录树就能够读到内存中,方便操作了,(比如用在云存储中),这里如果节点的名称为中文的话会报错,笔者尚未修改,也希望大家注意。(改也很简单,无非是编码问题,笔者会持续更新的。。。。。。)
这里的树形结构的节点,笔者选择了孩子-兄弟表示法,这样方便将普通树形结构转化成二叉树结构,这样操作二叉树的相关函数也可以用于操作普通的树形结构。
这里的节点结构及文件的枚举类型如下:
typedef enum fileType
{
file,
folder
}FileType;
typedef struct fileNode
{
char filename[MAXNAME];
FileType type;
struct fileNode* firstchild,*nextsibling,*parent;
}FileNode,*FileTree;
以下是相关代码,一共只有两个函数,一个用于判断当前节点是文件还是文件夹,一个用于将磁盘上的文件目录树读入到内存中。
代码中有这样几个问题希望读者们注意到:
1.fip = readdir(dp)
这段代码用来读取文件夹中的文件目录,每执行一次这段语句,fip指针都会顺次指向该目录下的文件,那么通过fip指针,我们可以判断其指向的文件是文件夹还是普通文件。
2.FileNode* rootNode, FileNode* preNode, FileNode* currentNode
以上几个变量分别是当前目录的根目录节点,记住是当前目录;fip指针上一次指向的节点;fip目录当前指向的节点。那么通过以上几个变量就能够有效地递归访问文件夹下的各个文件了。
3.sprintf_s(dirname, "%s\\%s", dir, currentNode->filename);
这里每一个node里存储的只是当前节点的文件名或者文件夹名,那么判断这个文件是否是文件夹,我们还是这个文件的绝对路径,也就是dir。
以下是代码;
//判断一个节点是文件还是文件夹
void identifyFileType(char* dir, FileNode* rootNode, FileNode* preNode, FileNode* currentNode, int depth);
//将文件目录树读入内存中
void reverseCatalogueToTree(char* dir, FileNode* rootNode, FileNode* preNode, FileNode* currentNode, int depth);
void identifyFileType(char* dir, FileNode* rootNode, FileNode* preNode, FileNode* currentNode, int depth)
{
char dirname[MAXNAME];
struct stat stbuf;
sprintf_s(dirname, "%s\\%s", dir, currentNode->filename);
if ((stat(dirname, &stbuf)) == -1)
{
fprintf(stderr, "listdirtree:can't read file %s information!\n", dirname);
return;
}
if (rootNode->firstchild == NULL){
rootNode->firstchild = currentNode;
currentNode->parent = rootNode;
}
else{
preNode->nextsibling = currentNode;
}
//printf("%s\n", currentNode->filename);
if ((stbuf.st_mode & S_IFMT) == S_IFDIR){
currentNode->type = FileType::folder;
reverseCatalogueToTree(dirname, currentNode, NULL, NULL, ++depth);
}
}
void reverseCatalogueToTree(char* dir, FileNode* rootNode, FileNode* preNode, FileNode* currentNode, int depth)
{
struct dirent *fip;
DIR *dp;
if ((dp = opendir(dir)) == NULL)
{
//fprintf(stderr,"can't open %s\n",dirname);
return;
}
while ((fip = readdir(dp)) != NULL)
{
if (strcmp(fip->d_name, ".") == 0 || strcmp(fip->d_name, "..") == 0)/*跳过"."或者".."的目录*/
{
continue;
}
currentNode = fileNodeFactory();
strcpy_s(currentNode->filename, MAXNAME, fip->d_name);
currentNode->parent = rootNode;
if (preNode == NULL)
{
preNode = fileNodeFactory();
}
else{
preNode->nextsibling = currentNode;
currentNode->parent = preNode->parent;
}
identifyFileType(dir, rootNode, preNode, currentNode, depth);
preNode = currentNode;
}
closedir(dp);
}
int main(int argc, char **argv)
{
FileTree tree = fileNodeFactory();
if (argc == 1){
strcpy_s(tree->filename, MAXNAME, ".");
reverseCatalogueToTree(".", tree, NULL, NULL, 0);
}
else{
printf("%s\n", argv[1]);
strcpy_s(tree->filename, MAXNAME, *++argv);
reverseCatalogueToTree(*argv, tree, NULL, NULL, 0);
}
return 0;
}
这里通过调试或者前序遍历就可以看到tree节点的内容了,这样某个根路径下的文件目录树就能够读到内存中,方便操作了,(比如用在云存储中),这里如果节点的名称为中文的话会报错,笔者尚未修改,也希望大家注意。(改也很简单,无非是编码问题,笔者会持续更新的。。。。。。)
如有问题,欢迎大家提出来,我的邮箱是kameleon@126.com
希望我分享的内容能够帮助到大家!!!