c – 如何对文件名进行排序?

我正在尝试编写一个模仿Unix中ls命令输出函数.我最初尝试使用scandir和alphasort来执行此操作,这确实打印了目录中的文件,并且确实对它们进行了排序,但由于某种原因,这个排序列表似乎与文件名的相同“排序列表”不匹配那给了.

例如,如果我有一个包含file.c,FILE.c和ls.c的目录.

ls按顺序显示它们:file.c FILE.c ls.c
但是当我使用alphasort / scandir对其进行排序时,它将它们排序为:FILE.c file.c ls.c

如何对目录中的文件进行排序,使其得到如此不同的排序结果?

解决方法:

要模拟认的ls -1行为,请通过调用使程序可识别区域设置

setlocale(LC_ALL, "");

靠近main()的开头,并使用

count = scandir(dir, &array, my_filter, alphasort);

其中my_filter()是一个函数,对于以点开头的名称返回0,对所有其他名称返回1. alphasort()一个POSIX函数,它使用locale归类顺序,与strcoll()的顺序相同.

基本实现是有用的

#define  _POSIX_C_SOURCE 200809L
#define  _ATFILE_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>

static void my_print(const char *name, const struct stat *info)
{
    /* Todo: Better output; use info too, for 'ls -l' -style output? */
    printf("%s\n", name);
}

static int my_filter(const struct dirent *ent)
{
    /* Skip entries that begin with '.' */
    if (ent->d_name[0] == '.')
        return 0;

    /* Include all others */
    return 1;
}

static int my_ls(const char *dir)
{
    struct dirent **list = NULL;
    struct stat     info;
    DIR            *dirhandle;
    int             size, i, fd;

    size = scandir(dir, &list, my_filter, alphasort);
    if (size == -1) {
        const int cause = errno;

        /* Is dir not a directory, but a single entry perhaps? */
        if (cause == ENOTDIR && lstat(dir, &info) == 0) {
            my_print(dir, &info);
            return 0;
        }

        /* Print out the original error and fail. */
        fprintf(stderr, "%s: %s.\n", dir, strerror(cause));
        return -1;
    }

    /* We need the directory handle for fstatat(). */
    dirhandle = opendir(dir);
    if (!dirhandle) {
        /* Print a warning, but continue. */
        fprintf(stderr, "%s: %s\n", dir, strerror(errno));
        fd = AT_FDCWD;
    } else {
        fd = dirfd(dirhandle);
    }

    for (i = 0; i < size; i++) {
        struct dirent *ent = list[i];

        /* Try to get information on ent. If fails, clear the structure. */
        if (fstatat(fd, ent->d_name, &info, AT_SYMLINK_nofollow) == -1) {
            /* Print a warning about it. */
            fprintf(stderr, "%s: %s.\n", ent->d_name, strerror(errno));
            memset(&info, 0, sizeof info);
        }

        /* Describe 'ent'. */
        my_print(ent->d_name, &info);
    }

    /* Release the directory handle. */
    if (dirhandle)
        closedir(dirhandle);

    /* discard list. */
    for (i = 0; i < size; i++)
        free(list[i]);
    free(list);

    return 0;
}

int main(int argc, char *argv[])
{
    int arg;

    setlocale(LC_ALL, "");

    if (argc > 1) {
        for (arg = 1; arg < argc; arg++) {
            if (my_ls(argv[arg])) {
                return EXIT_FAILURE;
            }
        }
    } else {
        if (my_ls(".")) {
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

请注意,为了您的目的,我故意使其比严格需要的更复杂,因为我不希望您只复制和粘贴代码.您可以更轻松地编译,运行和调查此程序,然后移植所需的更改 – 可能只是一个setlocale(“”,LC_ALL);线! – 到您自己的程序,而不是尝试向您的老师/讲师/ TA解释为什么代码看起来像是从其他地方逐字复制的.

上面的代码甚至适用于命令行中指定的文件(cause == ENOTDIR部分).它还使用单个函数my_print(const char * name,const struct stat * info)来打印每个目录条目;为此,它会为每个条目调用stat.

my_ls()不是构造目录条目的路径并调用lstat(),而是打开目录句柄,并使用fstatat(descriptor,name,struct stat *,AT_SYMLINK_nofollow)以与lstat()基本相同的方式收集信息. will,但name是从描述符指定的目录开始的相对路径(dirfd(句柄),如果handle是一个打开的DIR *).

确实,为每个目录条目调用一个stat函数是“慢”(特别是如果你执行/ bin / ls -1样式输出).然而,ls的输出是供人类消费的;并且经常或多或少地通过管道让人们悠闲地看待它.这就是为什么我个人不认为“额外”的stat()调用(即使不是真的需要)是一个问题.我所知道的大多数人类用户倾向于使用ls -l或(我最喜欢的)ls -laF –color = auto. (自动含义ANSI颜色仅在标准输出为终端时使用;即isatty时(fileno(stdout))== 1.)

换句话说,既然你有ls -1顺序,我建议你修改输出类似于ls -l(破折号,而不是破折号).您只需修改my_print()即可.

相关文章

用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2...
#!/bin/bashcommand1&command2&wait从Shell脚本并行...
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/ph...
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如...
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexa...
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全...