C 中的递归目录
我是 C 新手,尝试递归当前工作目录中的所有目录/文件并输出它们的信息。我遇到的问题是,当同一目录中有两个文件夹时,路径第二次构建错误,我想不出一个好方法来解决。例如,如果 dir1 和 dir2 在“/something/dir1”完成后位于同一路径中,则路径应变为“/something/dir2”,但由于我编写内容的方式而变为“/something/dir1/dir2”。我想只跟踪以前的路径,但我不确定有什么方法可以做到这一点,而不需要不断地重写每个递归调用。
更新:我已经修复了原来的错误,并认为我应该在这里发布我的新代码。我不知道的技巧是 opendir(".") 和 Changedir("..") 实际上会将句点转换为完整的当前或先前路径。至于将 type = 8 和 type = 4 语句更改为更具可读性的 S_ISDIR(statbuf.st_mode) 和 S_ISREG(statbuf.st_mode) 语句,它们似乎根本不起作用,而 type 语句却起作用。不确定语法和我尝试使用它们的方式有什么问题。
更新 2:我在这里解决了 S_ISDIR/S_ISREG 问题 - 如何使用 S_ISREG() 和 S_ISDIR() POSIX 宏?
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
void helper(DIR *, struct dirent *, struct stat, char *, int);
void dircheck(DIR *, struct dirent *, struct stat, char *, int);
int main(int argc, char *argv[]){
DIR *dip;
struct dirent *dit;
struct stat statbuf;
char currentPath[FILENAME_MAX];
int depth = 0; /*Used to correctly space output*/
dip = opendir(".");
getcwd(currentPath, FILENAME_MAX);
while((dit = readdir(dip)) != NULL){
/*Skips . and ..*/
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
stat(currentPath, &statbuf);
/*Checks if current item is of the type file (type 8)*/
if(dit->d_type == 8)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*Checks if current item is of the type directory (type 4)*/
if(dit->d_type == 4)
dircheck(dip, dit, statbuf, currentPath, depth);
}
return 0;
}
/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
int i = 0;
dip = opendir(currentPath);
while((dit = readdir(dip)) != NULL){
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
stat(currentPath, &statbuf);
if(dit->d_type == 8){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
if(dit->d_type == 4)
dircheck(dip, dit, statbuf, currentPath, depth);
}
}
void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
int i = 0;
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
/*If two directories exist at the same levelt the path
is built wrong and needs to be corrected*/
if((chdir(currentPath)) == -1){
chdir("..");
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
for(i = 0; i < depth; i++)
printf (" ");
printf("%s (subdirectory)\n", dit->d_name);
depth++;
helper(dip, dit, statbuf, currentPath, depth);
}
else{
for(i =0; i < depth; i++)
printf(" ");
printf("%s (subdirectory)\n", dit->d_name);
chdir(currentPath);
depth++;
helper(dip, dit, statbuf, currentPath, depth);
}
}
I'm new to C and trying to recurse through all directories/files from the current working directory and output their information. The problem I'm running into that I can't think of a good way to solve is when there are two folders in the same directory the path gets built wrong the second time. For instance if dir1 and dir2 are in the same path after "/something/dir1" is completed the path should become "/something/dir2" but becomes "/something/dir1/dir2" because of the way I have things written. I thought of just keeping track of the previous path, but I'm not sure of a way to do that without it constantly being rewritten every recursive call.
Update: I have since fixed the original bug I was having and figured I'd post my new code here. The trick I was unaware about was opendir(".") and changedir("..") would actually translate the period into the full current or previous path. As far as changing the type = 8 and type = 4 statements to the more readable S_ISDIR(statbuf.st_mode) and S_ISREG(statbuf.st_mode) statements, they appear not to work at all while the type statements do. Not sure what's wrong with the syntax and the way I tried using them.
Update 2: I solved the S_ISDIR/S_ISREG problem here - How to use S_ISREG() and S_ISDIR() POSIX Macros?
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
void helper(DIR *, struct dirent *, struct stat, char *, int);
void dircheck(DIR *, struct dirent *, struct stat, char *, int);
int main(int argc, char *argv[]){
DIR *dip;
struct dirent *dit;
struct stat statbuf;
char currentPath[FILENAME_MAX];
int depth = 0; /*Used to correctly space output*/
dip = opendir(".");
getcwd(currentPath, FILENAME_MAX);
while((dit = readdir(dip)) != NULL){
/*Skips . and ..*/
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
stat(currentPath, &statbuf);
/*Checks if current item is of the type file (type 8)*/
if(dit->d_type == 8)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*Checks if current item is of the type directory (type 4)*/
if(dit->d_type == 4)
dircheck(dip, dit, statbuf, currentPath, depth);
}
return 0;
}
/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
int i = 0;
dip = opendir(currentPath);
while((dit = readdir(dip)) != NULL){
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
stat(currentPath, &statbuf);
if(dit->d_type == 8){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
if(dit->d_type == 4)
dircheck(dip, dit, statbuf, currentPath, depth);
}
}
void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
int i = 0;
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
/*If two directories exist at the same levelt the path
is built wrong and needs to be corrected*/
if((chdir(currentPath)) == -1){
chdir("..");
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
for(i = 0; i < depth; i++)
printf (" ");
printf("%s (subdirectory)\n", dit->d_name);
depth++;
helper(dip, dit, statbuf, currentPath, depth);
}
else{
for(i =0; i < depth; i++)
printf(" ");
printf("%s (subdirectory)\n", dit->d_name);
chdir(currentPath);
depth++;
helper(dip, dit, statbuf, currentPath, depth);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不要不必要地重新发明轮子。如果您使用的是 unixy 系统,
nftw
库函数(POSIX 中 XSI 选项组的一部分,意味着它几乎普遍可用)完全可以满足您的需求:http://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html
另一方面,如果您将此作为学习练习,或者您有一个(相当罕见的)实例,其中
nftw
无法满足您的需求(例如,如果您需要从多个目录执行目录递归)线程同时)继续调试您的解决方案。Don't needlessly reinvent the wheel. If you're on a unixy system, the
nftw
library function (part of the XSI option group in POSIX, meaning it's almost universally available) does exactly what you want:http://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html
On the other hand, if you're doing this as a learning exercise, or if you have a (moderately rare) instance where
nftw
won't suit your needs (for instance, if you need to perform directory recursion from multiple threads simultaneously) proceed to debug your solution.我要做的第一件事就是改变这一点:
为此:
请参阅
stat
联机帮助页 了解更多详细信息。其次,看起来您从未调用过 closeir。你需要免费的东西。
第三...与其将
opendir
/readdir
代码复制粘贴到两个地方,将这些多余的工作放入同一个函数中会更有价值。最后......每次递归时在堆栈上分配 MAX_PATH 会变得相当大。您可能需要考虑使用
malloc
和realloc
。但正如 @R.. 上面提到的,您应该尽可能地重复使用这些缓冲区。对于大型目录树,这将使用大量空间并且成本昂贵。The very first thing I would do is change this:
To this:
See the
stat
manpage for more detail.Second, it looks like you are never calling
closedir
. You need to free stuff.Third... Rather than copy-pasting your
opendir
/readdir
code in two places, it would be valuable to put this redundant work into the same function.And lastly.. Allocating
MAX_PATH
on the stack at each recursion will get rather large. You might want to consider usingmalloc
andrealloc
. But as @R.. mentions above, you should try to re-use these buffers where you can. For a large dir tree this will use a lot of space and be expensive.