如何基于f_readdir实现目录文件的前后切换(支持上下按键操作)
实现目录文件的前后切换(基于f_readdir的反向浏览)
由于f_readdir本身是单向遍历目录的,要实现向前切换文件的功能,常用两种可行方案,根据你的设备内存和目录规模选择:
方案1:预缓存所有目录项(推荐小目录场景)
一次性把目录里的所有文件信息读进内存缓存,通过索引直接切换前后文件,访问速度快,不用反复操作目录。
实现思路
- 初始化时调用
f_readdir遍历整个目录,将有效文件(跳过.和..)的信息存入数组或链表 - 维护一个当前文件的索引变量,按下键时索引+1,上键时索引-1(做边界判断,避免越界)
- 直接从缓存中取出对应索引的文件名即可
代码示例
#include "ff.h" #include <string.h> // 定义目录项缓存结构 typedef struct { char fname[256]; FILINFO fno; } DirItem; // 根据实际需求调整缓存大小 DirItem dir_cache[100]; int cache_count = 0; int current_idx = -1; // 预加载目录缓存 void load_dir_cache(const char* dir_path) { DIR dir; FILINFO fno; cache_count = 0; current_idx = -1; if (f_opendir(&dir, dir_path) != FR_OK) return; while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0] != 0) { // 跳过当前目录和上级目录 if (strcmp(fno.fname, ".") == 0 || strcmp(fno.fname, "..") == 0) { continue; } // 存入缓存 strcpy(dir_cache[cache_count].fname, fno.fname); dir_cache[cache_count].fno = fno; cache_count++; } f_closedir(&dir); // 默认选中第一个文件 if (cache_count > 0) current_idx = 0; } // 获取上一个文件 const char* get_prev_file() { if (cache_count == 0 || current_idx <= 0) { return NULL; // 已是第一个文件或无文件 } current_idx--; return dir_cache[current_idx].fname; } // 获取下一个文件 const char* get_next_file() { if (cache_count == 0 || current_idx >= cache_count - 1) { return NULL; // 已是最后一个文件或无文件 } current_idx++; return dir_cache[current_idx].fname; }
方案2:反向遍历(适合大目录场景)
如果目录文件太多,缓存占用内存过高,可以每次按上键时重新扫描目录,找到当前文件的前一个文件。
实现思路
- 维护当前文件名的变量
- 按上键时,重新打开目录,从头调用
f_readdir遍历,记录当前文件的前一个文件信息 - 遍历到当前文件时停止,返回之前记录的文件名
- 注意边界处理(当前是第一个文件时,上键无效)
代码示例
#include "ff.h" #include <string.h> char current_fname[256] = {0}; // 获取上一个文件 const char* get_prev_file(const char* dir_path) { if (current_fname[0] == 0) return NULL; // 无当前文件 DIR dir; FILINFO fno; char prev_fname[256] = {0}; int found_current = 0; if (f_opendir(&dir, dir_path) != FR_OK) return NULL; while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0] != 0) { if (strcmp(fno.fname, ".") == 0 || strcmp(fno.fname, "..") == 0) { continue; } if (found_current) break; // 已找到当前文件的前一个,退出遍历 if (strcmp(fno.fname, current_fname) == 0) { found_current = 1; } else { strcpy(prev_fname, fno.fname); } } f_closedir(&dir); if (found_current && prev_fname[0] != 0) { strcpy(current_fname, prev_fname); return current_fname; } return NULL; // 已是第一个文件 } // 获取下一个文件(保留原f_readdir逻辑) const char* get_next_file(const char* dir_path) { static DIR dir; static int dir_opened = 0; FILINFO fno; if (!dir_opened) { if (f_opendir(&dir, dir_path) != FR_OK) return NULL; dir_opened = 1; // 跳过.和.. while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0] != 0) { if (strcmp(fno.fname, ".") != 0 && strcmp(fno.fname, "..") != 0) { strcpy(current_fname, fno.fname); return current_fname; } } dir_opened = 0; return NULL; } else { while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0] != 0) { if (strcmp(fno.fname, ".") != 0 && strcmp(fno.fname, "..") != 0) { strcpy(current_fname, fno.fname); return current_fname; } } dir_opened = 0; return NULL; } }
注意事项
- 缓存法适合文件数量少的目录,访问速度快,但如果目录内容动态变化(比如外部修改了目录),需要重新调用
load_dir_cache刷新缓存 - 反向遍历法不占额外内存,但每次上键都要重新扫描目录,速度稍慢,适合大目录场景
- 可根据需求添加循环浏览逻辑(比如到最后一个文件时按下键回到第一个,第一个文件按上键到最后一个)
内容的提问来源于stack exchange,提问作者N N
相关产品推荐
相关产品推荐

