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);
}
}