该程序用于读取指定目录 TXT_DIR 下的 .txt 文件名,并按照文件名中的数字顺序排序后打印。实现风格采用 C 语言方式:
- 使用
char*和char** - 使用
malloc/realloc/free - 使用
qsort排序 - 使用 POSIX 目录读取接口
opendir/readdir/closedir
cd /home/kali/TermVedioPlayer/PlayerOnTerm
g++ ConvertFile.cpp -o ConvertFile -std=c++17 -O2 -Wall
./ConvertFile注意:程序当前假设
TXT_DIR为../b025fe84-7fa2-4ad3-95e6-f3348fc00a1b,并且目录内含.txt文件。
flowchart TD
A[main()] --> B[GetAllFileNames(TXT_DIR, &names, &n)]
B --> C{read directory entries}
C -->|skip . / ..| C
C -->|is directory| B
C -->|is file| D[IsTxtFile(entry->d_name)]
D -->|yes| E[copy relative path to malloc buffer]
E --> F[realloc names array]
F --> G[append file path]
C -->|no| C
B --> H[qsort(names, n, sizeof(char*), CompareByFrameNumber)]
H --> I[print sorted names]
I --> J[FreeNames(names, n)]
J --> K[exit]
int main() {
char **names = NULL;
int n = 0;
if (GetAllFileNames(TXT_DIR, &names, &n) != 0) {
fprintf(stderr, "error opening dir: %s\n", TXT_DIR);
return 1;
}
printf("total %d files\n", n);
qsort(names, n, sizeof(char *), CompareByFrameNumber);
for (int i = 0; i < n; i++) {
printf("%s\n", names[i]);
}
FreeNames(names, n);
return 0;
}- 初始化
names为NULL,n为文件数量计数。 - 调用
GetAllFileNames收集目标目录下符合条件的文件名。 - 调用
qsort对names数组进行排序。 - 遍历排序结果并打印每个文件名。
- 释放
names数组和每个字符串占用的内存。
char **names适合保存 C 风格字符串数组,并与qsort兼容。- 把目录查找和排序逻辑分离,提高程序结构清晰度。
- 统一释放内存,避免资源泄漏。
int GetAllFileNames(const char *dirpath, char ***names, int *count) {
DIR *dir = opendir(dirpath);
if (dir == NULL) {
return -1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) {
continue;
}
char full_path[4096];
snprintf(full_path, sizeof(full_path), "%s/%s", dirpath, entry->d_name);
struct stat stat_buf;
if (stat(full_path, &stat_buf) == -1) {
continue;
}
if (S_ISDIR(stat_buf.st_mode)) {
GetAllFileNames(full_path, names, count);
} else {
if (!IsTxtFile(entry->d_name)) {
continue;
}
const char *relative = full_path + strlen(TXT_DIR);
char *copy = (char *)malloc(strlen(relative) + 1);
if (copy == NULL) {
closedir(dir);
return -1;
}
strcpy(copy, relative);
char **tmp =
(char **)realloc(*names, (*count + 1) * sizeof(char *));
if (tmp == NULL) {
free(copy);
closedir(dir);
return -1;
}
*names = tmp;
(*names)[*count] = copy;
(*count)++;
}
}
closedir(dir);
return 0;
}- 打开目录
dirpath。 - 逐项读取目录内容。
- 跳过特殊目录
.和..。 - 通过
stat判断目录项类型。 - 如果是目录,则递归进入子目录。
- 如果是普通文件且扩展名为
.txt,则把相对路径复制到内存中,并追加到names数组。
opendir/readdir/closedir是 POSIX 目录遍历标准,适用于 UNIX/Linux。- 递归处理目录可支持多层目录结构。
- 通过
realloc动态扩容数组,避免预先估算文件数量。 - 使用独立的
malloc复制缓冲区,确保每个文件名都可单独释放。
int IsTxtFile(const char *name) {
size_t len = strlen(name);
return len > 4 && strcmp(name + len - 4, ".txt") == 0;
}- 判断文件名长度是否大于 4。
- 比较最后 4 个字符是否为
.txt。
- 简单直接地判断后缀,避免复杂的字符串处理。
- 适用于文件名后缀固定的情况。
void FreeNames(char **names, int count) {
for (int i = 0; i < count; i++) {
free(names[i]);
}
free(names);
}- 逐个释放
names数组中保存的每个字符串内存。 - 最后释放
names数组本身。
- 每个字符串由
malloc分配,必须逐个释放。 names数组本身由realloc或malloc分配,也必须释放。- 这是标准 C 内存管理方式。
int CompareByFrameNumber(const void *a, const void *b) {
const char *const *pa = (const char *const *)a;
const char *const *pb = (const char *const *)b;
const char *sa = *pa;
const char *sb = *pb;
const char *dotA = strrchr(sa, '.');
const char *dotB = strrchr(sb, '.');
if (dotA == NULL || dotB == NULL || dotA - sa < 4 || dotB - sb < 4) {
return strcmp(sa, sb);
}
char bufA[5] = {0};
char bufB[5] = {0};
memcpy(bufA, dotA - 4, 4);
memcpy(bufB, dotB - 4, 4);
int numA = atoi(bufA);
int numB = atoi(bufB);
if (numA < numB)
return -1;
if (numA > numB)
return 1;
return strcmp(sa, sb);
}- 将
qsort传入的void *指针转换为const char *const *。 - 提取两个文件名中最后一个
.的位置。 - 如果文件名没有后缀或格式异常,使用普通字符串比较返回结果。
- 否则取
.前 4 个字符作为数字部分,转换为整数进行比较。 - 如果数字相同,则回退到字符串比较。
qsort回调必须符合int (*)(const void *, const void *)。- 通过指针解引用实现对
char*元素的比较。 - 按文件名中的数字排序,更符合
frame_0001.txt类型文件的排序需求。 - 次级比较保留稳定排序行为。
本程序设计思想:
- 使用 C 风格内存和字符串处理,减少 C++ 特性依赖。
- 使用
qsort与CompareByFrameNumber完成自定义排序。 - 使用递归目录遍历支持子目录查找。
- 使用显式内存管理保证运行后不会泄漏。