C语言 按名称查找文件并打印其属性的最有效方法是什么?

jdg4fx2g  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(182)

对不起,如果标题不是很合适,我实际上有多个问题,我无法将它们分开。
我写了一个在macOS上运行的命令行“find”程序的简化版本,它只是在指定路径中查找文件名的每个示例,并打印相应的文件路径(如果有的话)和最后修改的日期。这是一个单线程程序。我使用了readdir和lstat来获取必要的文件信息。
然后我比较了我的程序的性能,让我们称之为“myfind”(打印文件路径和最后修改时间),以“find”(与“find path -name filename”一起使用,只打印结果文件路径)。“myfind”用了100万个40秒,而“find”只用了40秒。在连续的尝试中,两次都是一致的。
对结果不满意,我做了一些“优化”,但没有做任何事情(比如删除递归,使用一个简单的堆栈来保存一些变量)。
这是我写的代码,删除了错误检查(执行或不执行错误检查不会改变执行时间):

struct recstruct{
    DIR *st_dir;
    char pathname[512];
};
//usage: ./execname path filename
int main(int argc, char *argv[]){
    chdir(argv[1]);
    myfind(argv[2]);
    return 0;
}
int myfind(char *name){
    DIR *curr_dir = opendir(".");
    int stk_i = 0;
    struct recstruct stk[128];
    struct dirent *next_entry;
    struct stat info;
    char *entry_name;
    char path[512];
    errno = 0;
    //ignores "." and ".."
    readdir(curr_dir);
    readdir(curr_dir);

    //gets next directory entry until it has fully explored the path
    while((next_entry = readdir(curr_dir)) != NULL || stk_i != 0){
        //goes back to previous directory if there is any and it's done with the current one
        if(next_entry == NULL){
            closedir(curr_dir);
            stk_i--;
            chdir(stk[stk_i].pathname);
            curr_dir = stk[stk_i].st_dir;
            continue;
        }
        //gets next directory entry and checks if its name is the one we are looking for
        entry_name = next_entry->d_name;
        lstat(entry_name, &info);
        if(strncmp(name, entry_name, 127) == 0){
            realpath(entry_name, path);
            printf("path:%s\nlast modified:%s", path, ctime(&(info.st_mtime)));
        }
        //if next_entry is a dir, save current environment and explore the new dir
        if(S_ISDIR(info.st_mode)){ 
            getcwd(stk[stk_i].pathname, 512);
            stk[stk_i].st_dir = curr_dir;
            stk_i++;
            chdir(entry_name);
            curr_dir = opendir(".");
            readdir(curr_dir);
            readdir(curr_dir);
        }
    }
    closedir(curr_dir);
    return 0;
}

然后,通过删除所有lstat调用,从“myfind”中删除了打印最后修改日期的部分。执行时间降到了30秒,在连续调用(来自终端的程序调用,而不是函数调用)时降到了大约3-6秒。即使在关闭和重新开放航站楼后,改善仍然存在)。
这种行为还改善了在目录树中查找文件关闭的连续调用的执行时间,这似乎是这种类型的程序的可能使用场景,所以我希望能够更多地理解它。
所以我想问的是
1.这种对连续调用的执行时间的改进是如何确切地起作用的?为什么在使用lstat时,“find”和“myfind”中都没有它?
1.有没有其他的东西我可以改变我的代码,使它更优化,同时使它也打印最后修改日期?我也可以让它依赖于平台。
我在SSD上操作,每次执行只查找1个文件名

xwbd5t1u

xwbd5t1u1#

下面是@EricPostpischil建议的fts_实现。它假设用户提供了一个目录,如果你想验证在循环之前执行第一个fts_read()并检查fts_info。它还假设文件名是一个基本名(不包含任何斜杠)。你也可以检查一下。
查看fts_open()的选项,确保这是您想要的行为。
它只是检查文件名是否与常规文件匹配; find -name也将匹配目录名称。很容易在for-循环之前解决这个问题。

#include <fts.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

int myfind(char *const path, const char *filename) {
    FTS *ftsp = fts_open(
        (char *const []) { path, NULL},
        FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT,
        NULL
    );
    if(!ftsp) {
        perror("");
        goto err;
    }
    for(FTSENT *ftsent = fts_read(ftsp); ftsent; ftsent = fts_read(ftsp)) {
        if(ftsent->fts_info & FTS_D)
            for(ftsent = fts_children(ftsp, FTS_NAMEONLY); ftsent; ftsent = ftsent->fts_link)
                if(!strcmp(filename, ftsent->fts_name))
                    printf("%s%s\n", ftsent->fts_path, ftsent->fts_name);
    }
    if(errno) {
        perror("");
        goto err;
    }
    fts_close(ftsp);
    return EXIT_SUCCESS;
err:
    if(ftsp)
        fts_close(ftsp);
    return EXIT_FAILURE;
}

int main(int argc, char *argv[]){
    if(argc != 3) {
        printf("usage: path filename\n");
        return 0;
    }
    return myfind(argv[1], argv[2]);
}

下面是一些运行示例(使用热文件缓存进行比较):

$ time find . -name non-existing_file
real    0m5.296s
user    0m0.865s

$ time ./myfind . non-existing_file
real    0m4.259s
user    0m0.942s
$ time find  . -name AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1670391304.3514712-709042-260172808017632/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1658890524.6326995-109370-280029288970992/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1567991519.7013745-230514371888007/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1658888271.8413315-108112-177019459387621/AnsiballZ_file.py

real    0m5.445s
user    0m0.847s
sys     0m0.961s

$ time ./myfind . AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1670391304.3514712-709042-260172808017632/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1658890524.6326995-109370-280029288970992/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1567991519.7013745-230514371888007/AnsiballZ_file.py
./.ansible/tmp/ansible-tmp-1658888271.8413315-108112-177019459387621/AnsiballZ_file.py

real    0m7.481s
user    0m0.882s
sys     0m2.713s

我看到~ 3. 9到~ 7. 6真实的运行在我的系统上,所以应该是可比的发现。

相关问题