如何对读取的目录文件排序?模仿Unix dir命令的实现疑问
问题
我想要编写一个模仿Unix Shell中dir命令的程序,目前已实现读取当前目录的功能(代码如下),但不清楚如何对文件进行排序以匹配dir命令的排序效果。我疑惑是否需要将所有文件名存入字符串数组后使用选择排序或其他排序方法,同时想了解dir命令在打印文件前的排序规则。
#include <stdio.h> #include <string.h> #include <dirent.h> #include <stdlib.h> #include <string.h> void dirfunction() { DIR* directory=opendir("."); if(directory==NULL) { perror("Directory does not exist"); exit(1); } struct dirent* p; p=readdir(directory); int i=0; while(p!=NULL) { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { printf("%s ",p->d_name); i++; } p=readdir(directory); } printf("\n"); closedir(directory); } int main(int argc,char** argv[]) { dirfunction(); }
回答
一、dir命令的排序规则
Unix系统中dir命令(和ls默认行为一致)的排序遵循当前系统区域设置(locale)的字典序,核心特点:
- 若使用
Clocale,会严格按ASCII码值排序(大写字母在小写字母前,因为'A'-'Z'的ASCII码小于'a'-'z');在en_US.UTF-8这类locale下,是不区分大小写的字典序。 - 以
.开头的隐藏文件默认排在非隐藏文件之前(你的代码已过滤.和..,若要保留其他隐藏文件,它们会处于前列)。 - 基于文件名完整字符串比较,包含扩展名在内的所有字符都会参与排序。
二、排序实现方案
确实需要先收集所有文件名到数组中再排序,推荐使用标准库的qsort(比手动实现的选择排序效率更高、更稳定),具体步骤:
- 遍历目录,将需要显示的文件名存入动态数组;
- 用
qsort结合符合locale规则的比较函数完成排序; - 遍历排序后的数组打印文件名。
修改后的代码如下:
#include <stdio.h> #include <string.h> #include <dirent.h> #include <stdlib.h> #include <locale.h> // 适配locale的文件名比较函数,供qsort使用 int compare_filenames(const void *a, const void *b) { const char *name1 = *(const char **)a; const char *name2 = *(const char **)b; // strcoll遵循当前locale的排序规则,替代ASCII码比较的strcmp return strcoll(name1, name2); } void dirfunction() { DIR* directory = opendir("."); if (directory == NULL) { perror("Directory does not exist"); exit(EXIT_FAILURE); } struct dirent* p; char **filenames = NULL; int count = 0; int capacity = 0; // 收集所有需要显示的文件名 while ((p = readdir(directory)) != NULL) { if (strcmp(p->d_name, ".") != 0 && strcmp(p->d_name, "..") != 0) { // 动态扩容数组,避免固定大小限制 if (count >= capacity) { capacity = (capacity == 0) ? 8 : capacity * 2; filenames = realloc(filenames, capacity * sizeof(char *)); if (filenames == NULL) { perror("Failed to allocate memory"); closedir(directory); exit(EXIT_FAILURE); } } // 复制文件名到数组(readdir返回的d_name指向内部缓冲区,后续会被覆盖) filenames[count] = strdup(p->d_name); if (filenames[count] == NULL) { perror("Failed to duplicate string"); // 释放已分配的内存,避免泄漏 for (int i = 0; i < count; i++) { free(filenames[i]); } free(filenames); closedir(directory); exit(EXIT_FAILURE); } count++; } } closedir(directory); // 排序并打印文件名 if (count > 0) { qsort(filenames, count, sizeof(char *), compare_filenames); for (int i = 0; i < count; i++) { printf("%s ", filenames[i]); free(filenames[i]); } printf("\n"); } free(filenames); } int main(int argc, char **argv) { // 启用系统默认locale,保证排序规则和dir命令一致 setlocale(LC_COLLATE, ""); dirfunction(); return EXIT_SUCCESS; }
关键细节说明
- 使用
strcoll而非strcmp:strcmp仅按ASCII码比较,strcoll会适配系统locale,和dir的排序逻辑完全对齐。 - 动态数组:自动扩容适配目录文件数量,避免固定大小数组的局限性。
- 内存管理:用
strdup复制文件名防止缓冲区覆盖,使用完毕后逐一释放内存,避免泄漏。 setlocale(LC_COLLATE, ""):让程序使用系统默认区域设置,确保排序行为和系统原生dir一致。
内容的提问来源于stack exchange,提问作者Marian
相关产品推荐
相关产品推荐

