之前講述了如何利用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);
}
}