下面的 C 代码将列出文件和目录的数量,并且比 linux find 命令快 4 倍。我只需要文件夹的数量,对文件数量甚至列出它们不感兴趣。有没有办法优化下面的代码并使其更加高效?
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
void listdir(char *path, size_t size) {
DIR *dir;
struct dirent *entry;
size_t len = strlen(path);
if (!(dir = opendir(path))) {
fprintf(stderr, "path not found: %s: %s\n",
path, strerror(errno));
return;
}
puts(path);
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) {
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if (len + strlen(name) + 2 > size) {
fprintf(stderr, "path too long: %s/%s\n", path, name);
} else {
path[len] = '/';
strcpy(path + len + 1, name);
listdir(path, size);
path[len] = '\0';
}
} else {
printf("%s/%s\n", path, name);
}
}
closedir(dir);
}
int main( int argc, char *argv[] ) {
if( argc == 2 ) {
printf("Path: %s\n", argv[1]);
}
else if( argc > 2 ) {
printf("Too many arguments supplied.\n");
}
else {
printf("One argument expected.\n");
return 0;
}
char path[1024];
memcpy (path, argv[1],1024);
listdir(path, sizeof path);
return 0;
}
删除以下行当然不会显示文件,但不会加快执行时间:
} else {
printf("%s/%s\n", path, name);
}
最佳答案
如果您对打印文件名不感兴趣,只需删除 printf
语句即可。
但是请注意,代码中存在一些问题:
memcpy(path, argv[1], 1024);
可能会读取超出argv[1]
指向的字符串末尾的内容,这是未定义的行为,或者无法生成正确的 C 字符串,这会导致函数listdir
中出现未定义的行为。
您还可以避免在每次递归调用中重新计算目录名称的长度。
这是您可以尝试的修改版本:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
long long countdirs(char *path, size_t size, size_t len) {
DIR *dir;
struct dirent *entry;
long long count;
if (!(dir = opendir(path))) {
fprintf(stderr, "path not found: %s: %s\n",
path, strerror(errno));
return 0;
}
count = 1; // count this directory
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char *name = entry->d_name;
size_t len1 = strlen(name);
if (*name == '.' && (len1 == 1 || (len1 == 2 && name[1] == '.')))
continue;
if (len + len1 + 2 > size) {
count++;
fprintf(stderr, "path too long: %s/%s\n", path, name);
} else {
path[len] = '/';
memcpy(path + len + 1, name, len1 + 1);
count += countdirs(path, size, len + 1 + len1);
path[len] = '\0';
}
}
}
closedir(dir);
return count;
}
int main(int argc, char *argv[]) {
char buf[4096];
char *path;
size_t len;
if (argc != 2) {
fprintf(stderr, "one argument expected.\n");
return 1;
}
path = argv[1];
len = strlen(path);
if (len >= sizeof(buf)) {
fprintf(stderr, "path too long: %s\n", path);
return 1;
}
memcpy(buf, path, len + 1);
printf("%s: %lld directories\n", path, countdirs(buf, sizeof buf, len));
return 0;
}
进一步说明:
如果目录树太深或有循环,上面的代码可能会失败。失败可能是由于句柄耗尽导致
opendir
失败。您应该尝试使用 POSIX 标准函数
nftw()
的替代方法,如本答案所述:https://stackoverflow.com/a/29402705/4593267正如 EOF 所建议的,由于不使用路径,因此不需要构建它们。使用
openat()
和fdopendir()
可能更安全、更高效。 (记录在这里:https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html)。优化此功能没有什么意义,因为大部分时间都花在操作系统或等待存储设备上。文件系统缓存的影响可能是巨大的:我在 Linux 上测量了 133000 个目录的 15 倍。使用不同的系统调用集可能会提高或降低速度,但小的改进可能是高度系统特定的。
关于c - 在 Linux 中递归计算目录的最快 C 代码(无文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56811153/