Linux C/C++ 利用scandir和alphasort遍历目录文件并排序

之前讲述了如何利用readdir/readdir_r,对指定目录进行遍历并输出,参见:Linux C 讲解系统调用readdir, readdir_r 以及如何遍历目录下的所有文件

这里讲述利用scandir和alphasort如何遍历指定目录,并对文件名排序输出。

scandir,alphasort,versionsort

scandir,alphasort,versionsort 可搭配用于扫描指定目录dirp(不含子目录)下,满足filter过滤模式的文件,返回的结果通过qsort排序存放到namelist数组中(由scandir函数调用malloc分配空间),比较子用的是compar。

函数原型

查看man scandir(3)

#include <dirent.h>

int scandir(const char *dirp, struct dirent ***namelist,
      int (*filter)(const struct dirent *),
      int (*compar)(const struct dirent **, const struct dirent **));

int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);

alphasort和versionsort

alphasort和versionsort 作为比较函数,原型相同,可传递给compar。其区别是,
1)遵循的标准不一样
scandir,alphasort和遵循POSIX.1-2008;
versionsort是GNU扩展;
也就是说,两者需要的宏定义不一样:alphasort需要_BSD_SOURCE || _SVID_SOURCE支持,versionsort需要_GNU_SOURCE支持。

2)实现时,调用的比较函数不一样
alphasort调用的是strcoll,versionsort调用的是strverscmp。

  • 对于strcoll:
    比较是基于字符串被解释为,适合当前语言环境的LC_COLLATE类别,也就是说大多数情况下,字符串是ASCII编码。

  • 对于strverscmp:
    strcmp会将文件jan1, jan2, ..., jan9, jan10,排成字典序jan1, jan10, ..., jan2, ..., jan9。而strverscmp将里面的数字做了修正,会得到jan1, jan2, ..., jan9, jan10这样的排序。
    其比较的字符串并非LC_COLLATE

  • LC_COLLATE的描述参见man setlocale(3)

LC_COLLATE
              for regular expression matching (it determines the  meaning  of  range
              expressions and equivalence classes) and string collation.

翻译一下:

对于正则表达式匹配 (它决定了范围表达式和等价类的含义)和字符串校勘。

从这里看不出什么,可参见这篇文章setlocale()函数详解——C语言,可知,用setlocale设置LC_COLLATE会影响strcoll和strxfrm,其C字符串是ASCII编码。

示例

用scandir和alphasort,scandir和versionsort 分别顺序、逆序打印指定目录下的文件名。
注意:打印文件的顺序,跟使用的比较函数无关,而是取决于变量存放结果的namelist的访问顺序。

#include <dirent.h>
#include <stdio.h>

/* 顺序打印指定目录下文件 */
void test_scandir1()
{
    struct dirent **namelist;
    int n;
    n = scandir(".", &namelist, NULL, alphasort);
    if (n < 0)
        perror("scandir error");
    else
    {
        for (int m = 0; m < n; ++m)
        {
            printf("%s\n", namelist[m]->d_name);
            free(namelist[m]);
        }
        free(namelist);
    }
}

/* 逆序打印指定目录下文件 */
void test_scandir2()
{
    struct dirent **namelist;
    int n;
    n = scandir(".", &namelist, NULL, versionsort);
    if (n < 0)
        perror("scandir error");
    else
    {
        while (n--)
        {
            printf("%s\n", namelist[n]->d_name);
            free(namelist[n]);
        }
        free(namelist);
    }
}
posted @ 2022-02-07 23:36  明明1109  阅读(4217)  评论(0编辑  收藏  举报