stat()函数目录识别异常:部分目录被误判为文件
问题描述
使用stat.h头文件和stat()函数读取并列出目录中的文件与目录时,出现以下异常:
- 仅能识别当前目录和2个子目录为目录,其余路径正确的目录均被判定为文件
- 预设目录中新增的目录无法被识别,但嵌套在已识别目录中的新增目录可正常识别
怀疑是stat函数使用不当导致问题,以下是两段测试用C代码(IDE为Dev-C++):
第一段测试代码
#include <stdio.h> #include <errno.h> #include <stdlib.h> #include <dirent.h> #include <locale.h> #include <sys/stat.h> #include <wchar.h> extern int errno; int main(int argc, char *argv[]) { setlocale(LC_ALL,"Turkish"); DIR* dp; struct dirent dirp; char test[9999]; getcwd(test,9999); printf("FOLDERMAIN:%s\n",test); dp=opendir("."); searchDirectoryForString(dp,"Mat",test); return 0; } void searchDirectoryForString(DIR* dp,char* str,char* directoryname) { int lineno=1,num=0; FILE* fp; struct dirent *file; char temp[99999]; char buf[99999]; char *rpath; rpath = calloc(99999,1); char path[99999]; char* temppath; temppath = calloc(99999,1); int count = 0; char dirhold[100]; char* dot; int line_num = 1; int find_result=0; static int check=0; DIR* fdp; if(dp==NULL) { printf("cant open"); } struct stat filestat; while (file=readdir(dp)) { stat(file->d_name,&filestat); if(strcmp(file->d_name,"..")==0||(strcmp(file->d_name,".")==0)) continue; if ( S_ISDIR(filestat.st_mode)) { check++; printf("\n CHECK: %d\n",check); if(check!=1) { strcpy(temppath,directoryname); printf("temp:%s\n",directoryname); strcat(directoryname,"\\"); strcat(directoryname,file->d_name); dot = strrchr(directoryname, '\\'); strcpy(dirhold,dot); printf("DIR: %s\n",dirhold); printf("%s KLASÖR\n",directoryname); chdir(directoryname); dp=opendir(directoryname); if(dp==NULL) { printf("2\n"); } strcat(temppath,dirhold); searchDirectoryForString(dp,str,temppath); } else{ strcpy(path,directoryname); strcat(directoryname,"\\"); strcat(directoryname,file->d_name); dot = strrchr(directoryname, '\\'); strcpy(dirhold,dot); printf("DIR: %s\n",dirhold); printf("%s FOLDER\n",directoryname); chdir(directoryname); dp=opendir(directoryname); if(dp==NULL) { printf("2\n"); } strcat(path,dirhold); searchDirectoryForString(dp,str,path); } } else { snprintf(buf,99999,"%s",directoryname); strcat(buf,"\\"); strcat(buf,file->d_name); printf("%s FILE \n",buf); if(fp) { fclose(fp); strcpy(rpath," "); } } } }
第二段MCVE代码
#include <locale.h> #include <unistd.h> #include <dirent.h> #include <stdio.h> #include <sys/stat.h> #include <wchar.h> int main(int argc, char *argv[]) { setlocale(LC_ALL,"Turkish"); DIR* dp; struct dirent dirp; char test[9999]; getcwd(test,9999); printf("FOLDERMAIN:%s\n",test); dp=opendir("."); listDir(dp,test); return 0; } int isDir(const char *name) { struct stat fileStat; return !stat(name, &fileStat) && S_ISDIR(fileStat.st_mode); } void listDir(DIR* dp,char* directoryname) { struct dirent *file; char buf[99999]; char* temppath; temppath = calloc(99999,1); int count = 0; char dirhold[100]; char* dot; int line_num = 1; int find_result=0; static int check=0; char* dir; DIR* fdp; if(dp==NULL) { printf("cant open"); } while (file=readdir(dp)) { if(strcmp(file->d_name,"..")==0||(strcmp(file->d_name,".")==0)) continue; if (isDir(file->d_name)) { check++; printf("\n CHECK: %d\n",check); strcpy(temppath,directoryname); printf("temp:%s\n",directoryname); strcat(directoryname,"\\"); strcat(directoryname,file->d_name); dot = strrchr(directoryname, '\\'); strcpy(dirhold,dot); printf("DIR: %s\n",dirhold); printf("%s FOLDER\n",directoryname); chdir(directoryname); dp=opendir(directoryname); if(dp==NULL) { printf("2\n"); } strcat(temppath,dirhold); listDir(dp,temppath); } else { snprintf(buf,99999,"%s",directoryname); strcat(buf,"\\"); strcat(buf,file->d_name); printf("%s FILE\n",buf); } } }
问题根源与修复方案
核心问题
stat使用相对路径错误:调用stat(file->d_name, ...)时,仅传入文件名而非完整路径。当通过chdir切换目录后,后续stat会去当前工作目录查找原目录的文件,必然失败,导致filestat未初始化,误判为文件。- 修改传入的路径参数:直接对
directoryname做strcat操作,破坏了上层递归的路径值,导致路径混乱。 - 工作目录未恢复:
chdir进入子目录后未切回原目录,后续所有文件操作都基于子目录路径,完全偏离预期逻辑。
修复后的代码
#include <locale.h> #include <unistd.h> #include <dirent.h> #include <stdio.h> #include <sys/stat.h> #include <string.h> #include <stdlib.h> // 使用完整路径判断是否为目录,并添加错误处理 int isDir(const char *fullPath) { struct stat fileStat; if (stat(fullPath, &fileStat) == -1) { perror("stat error"); return 0; } return S_ISDIR(fileStat.st_mode); } void listDir(const char *currentDir) { DIR* dp = opendir(currentDir); if(dp == NULL) { perror("opendir error"); return; } struct dirent *file; char fullPath[1024]; char originalDir[1024]; // 保存当前工作目录,用于后续恢复 getcwd(originalDir, sizeof(originalDir)); while ((file = readdir(dp)) != NULL) { if(strcmp(file->d_name, "..") == 0 || strcmp(file->d_name, ".") == 0) continue; // 拼接完整路径,避免相对路径错误 snprintf(fullPath, sizeof(fullPath), "%s\\%s", currentDir, file->d_name); if (isDir(fullPath)) { printf("%s FOLDER\n", fullPath); // 递归遍历子目录 listDir(fullPath); } else { printf("%s FILE\n", fullPath); } } closedir(dp); // 切回原工作目录,保证后续路径正确 chdir(originalDir); } int main(int argc, char *argv[]) { setlocale(LC_ALL,"Turkish"); char currentDir[1024]; getcwd(currentDir, sizeof(currentDir)); printf("FOLDERMAIN:%s\n", currentDir); listDir(currentDir); return 0; }
关键修复点
- 传递完整目录路径给遍历函数,不再依赖
DIR*和工作目录切换 - 始终使用完整路径调用
stat,确保路径不受当前工作目录影响 - 保存并恢复工作目录,避免路径混乱
- 添加错误处理,方便排查
opendir/stat的失败原因
内容的提问来源于stack exchange,提问作者user20137453
相关产品推荐
相关产品推荐

