搜索
bottom↓
回复: 52

FATFS文件系统,采用非递归的方法遍历文件夹

[复制链接]

出0入0汤圆

发表于 2018-1-18 08:37:50 | 显示全部楼层 |阅读模式
本帖最后由 am3359 于 2018-1-18 17:02 编辑

//我用STM32F407+FATFS+freeRTOS搭建的系统,需要遍历存储器中每个文件夹和文件,常用的办法是递归调用,遍历函数内嵌套自己直到找到每个文件或文件夹。由于目录深度不可控,这个方法有大量的压栈和出栈容易造成栈溢出系统崩溃。
//现采用目录深度可控的非递归方法如下:

#define LEVEL          8                                       //LEVEL设置大小代表遍历的深度,8就代表8层,内存足够的话可以设置更大些
    u8 j,m;
    u8 l[LEVEL];                                               //l[]保存每层文件夹长度,返回上级目录时用
    DIR dir_a[LEVEL];                                          //FATFS使用的目录结构,只有这个比较占内存需要LEVEL*36字节
    tbuf[64];                                                  //注意tbuf的大小要能放得下最深的文件名绝对路径

    m = 0;
    j = 1;
    printf("当前目录:\r\n%s:\r\n", tbuf);
    while(1) {
        if ( j > m ) {                                         //只有搜索子目录时才执行
            f_opendir(&dir_a[j-1], (TCHAR*)tbuf);
            l[j-1] = strlen((char *)tbuf);
        }
        m = j;
        f_readdir(&dir_a[j-1], &fileinfo);                     //读取当前目录下的一个文件
        if (fileinfo.fname[0] == 0) {                          //到末尾了,退出
            if (j>1) j--;                                      //下个循环进入父目录
            else break;
            tbuf[l[j-1]] = '\0';                               //存储的路径返回上级目录
        } else {
            sprintf((char *)tbuf,"%s/%s",tbuf,*fileinfo.lfname ? fileinfo.lfname : fileinfo.fname);//搜索到的文件或文件名连接成完整的路径
            if (fileinfo.fattrib & AM_DIR) {                   //是目录
                printf("%s [%dD]\r\n", tbuf,j);                //打印目录
                if (j<8) j++;                                  //下个循环进入子目录
            } else {
                printf("%s [%dF]\r\n", tbuf,j);                //打印文件
                tbuf[l[j-1]] = '\0';                           //存储的路径返回目录
            }
        }
    }

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2018-1-18 08:41:29 | 显示全部楼层
本帖最后由 am3359 于 2018-1-18 09:27 编辑

结果如下,顶起

出0入0汤圆

 楼主| 发表于 2018-1-18 09:19:13 | 显示全部楼层
本帖最后由 am3359 于 2018-1-18 09:23 编辑

目录结构如下:                                遍历结果如下:
0:                                                   0:                                
|--TEST/                                           0:/TEST [1D]                     
|  |----111/                                       0:/TEST/111 [2D]                  
|  |----222                                        0:/TEST/222 [2F]                  
|  |----333/                                       0:/TEST/333 [2D]                  
|       |---111                                    0:/TEST/333/111 [3F]              
|       |---222                                    0:/TEST/333/222 [3F]              
|       |---aa/                                    0:/TEST/333/aa [3D]               
|           |--bb/                                 0:/TEST/333/aa/bb [4D]            
|              |--cc/                              0:/TEST/333/aa/bb/cc [5D]         
|                 |--dd/                           0:/TEST/333/aa/bb/cc/dd [6D]      
|                    |--ee/                        0:/TEST/333/aa/bb/cc/dd/ee [7D]   
|                       |--ff/                     0:/TEST/333/aa/bb/cc/dd/ee/ff [8D]
|                          |--gg/                  0:/AAA [1F]                       
|                             |--hh/               0:/CCC [1D]                       
|--AAA                                             0:/CCC/111 [2F]                  
|--CCC/                                            0:/CCC/222 [2F]                  
   |--111
   |--222

由于LEVEL设置的是8,所以从0:开始只搜索8层, 第9层文件夹gg和第10层文件夹hh不会去找

出0入0汤圆

 楼主| 发表于 2018-1-18 13:29:16 | 显示全部楼层
这么快沉了,昨天写到1:00才完成的

出0入0汤圆

发表于 2018-1-18 13:38:15 来自手机 | 显示全部楼层
支持一下

出0入8汤圆

发表于 2018-1-18 13:40:27 | 显示全部楼层
am3359 发表于 2018-1-18 13:29
这么快沉了,昨天写到1:00才完成的

帮你顶一下吧。
楼主辛苦了。

出0入0汤圆

 楼主| 发表于 2018-1-18 13:44:12 | 显示全部楼层
security 发表于 2018-1-18 13:40
帮你顶一下吧。
楼主辛苦了。

谢谢!!

出0入0汤圆

发表于 2018-1-18 14:12:01 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2018-1-18 14:35:47 | 显示全部楼层
谢谢分享,拿去用了

出50入0汤圆

发表于 2018-1-18 14:40:35 | 显示全部楼层
收藏,谢谢分享!

出0入0汤圆

发表于 2018-1-18 15:07:18 | 显示全部楼层
谢谢分享,辛苦了

出0入0汤圆

 楼主| 发表于 2018-1-18 16:18:56 | 显示全部楼层
为什么都是金牌会员?我10几年老用户还是注册会员

出0入8汤圆

发表于 2018-1-18 17:00:58 | 显示全部楼层
am3359 发表于 2018-1-18 16:18
为什么都是金牌会员?我10几年老用户还是注册会员

不要一直潜水,这些都是一步一脚印走出来的。

出0入0汤圆

 楼主| 发表于 2018-1-18 17:03:56 | 显示全部楼层
还差10分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:04:23 | 显示全部楼层
还差9分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:04:40 | 显示全部楼层
还差8分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:04:57 | 显示全部楼层
还差7分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:05:18 | 显示全部楼层
还差6分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:05:51 | 显示全部楼层
还差5分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:06:18 | 显示全部楼层
还差4分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:06:44 | 显示全部楼层
还差3分,就是中级会员了

出0入0汤圆

 楼主| 发表于 2018-1-18 17:07:04 | 显示全部楼层
还差2分,就是中级会员了

出0入0汤圆

发表于 2018-1-18 17:14:47 | 显示全部楼层
am3359 发表于 2018-1-18 09:19
目录结构如下:                                遍历结果如下:
0:                                      ...

帮顶,ZSBD

出0入14汤圆

发表于 2018-1-18 19:07:34 | 显示全部楼层
只看不回复,就变成你这样了

出0入0汤圆

 楼主| 发表于 2018-1-19 08:09:17 | 显示全部楼层
还差1分,就是中级会员了

出0入0汤圆

发表于 2018-1-19 09:04:11 来自手机 | 显示全部楼层
递归占用堆栈多是因为所用函数不是所有内部变量都是需要保存的。想省堆栈就在顶层定义一个结构体,递归调用函数操作这个结构体,函数内部仅申请必须的变量。

出0入0汤圆

发表于 2018-1-19 09:27:33 | 显示全部楼层
谢谢分享,不知楼主有没有试过littlefs?

出0入34汤圆

发表于 2018-1-19 09:58:39 | 显示全部楼层
好吧!支持一下楼主。话说约十年也在这 FatFS 上玩过这事儿,呵呵~

出0入0汤圆

 楼主| 发表于 2018-1-19 10:06:43 | 显示全部楼层
本帖最后由 am3359 于 2018-1-19 10:08 编辑
sblpp 发表于 2018-1-19 09:27
谢谢分享,不知楼主有没有试过littlefs?


我10年都卡在初学者阶段,FATFS,FREERTOS,都是2017年才学的,littlefs完全没听说过。实在是百度没找到C语言写的非递归的遍历方法才写了这个,找到我就直接COPY了

出0入0汤圆

 楼主| 发表于 2018-1-19 10:10:58 | 显示全部楼层
vuo50z 发表于 2018-1-19 09:04
递归占用堆栈多是因为所用函数不是所有内部变量都是需要保存的。想省堆栈就在顶层定义一个结构体,递归调用 ...

不懂,我没研究那么深啊,用了递归函数FreeRTOS就挂了,才想用非递归的办法,但是没找到

出0入4汤圆

发表于 2018-1-19 10:13:00 | 显示全部楼层
谢谢分享,拿去用了

出0入0汤圆

 楼主| 发表于 2018-1-19 10:16:51 | 显示全部楼层
随便用,欢迎指正

出0入0汤圆

发表于 2018-1-19 10:29:01 | 显示全部楼层
am3359 发表于 2018-1-19 10:10
不懂,我没研究那么深啊,用了递归函数FreeRTOS就挂了,才想用非递归的办法,但是没找到 ...

void test( void )
{
        int i = 0;                        // 这个i需要保存
        char acFileName[256];        // 这个buffer不需要保存。可以定义为全局的,或者根函数申请一个然后通过参数传下来,这样就不占用堆栈了。

        while( 1 )
        {
                i ++;                                        // 本层循环计数

                GetName( acFileName );        // buffer打印完就没用了
                printf( "%s %d", acFileName, i );

                if( strlen( acFileName  ) == 0 )
                {
                        return;
                }

                test();
        }
}

出0入0汤圆

发表于 2018-1-19 10:33:06 | 显示全部楼层
vuo50z 发表于 2018-1-19 10:29
void test( void )
{
        int i = 0;                        // 这个i需要保存

上面那个函数每次递归都要占用256字节的buffer,改成下面这样,只在main中申请一个256字节就可以了。

void test( char *pcFileName )
{
        int i = 0;                        // 这个i需要保存

        while( 1 )
        {
                i ++;                                                // 本层循环计数

                GetName( pcFileName);                // buffer打印完就没用了
                printf( "%s %d", pcFileName, i );

                if( strlen( pcFileName) == 0 )
                {
                        return;
                }

                test( pcFileName );
        }
}

int main( void )
{
        char acFileName[256];        // 这个buffer不需要保存。可以定义为全局的,或者根函数申请一个然后通过参数传下来,这样就不占用堆栈了。

        test( acFileName );
}

出0入0汤圆

 楼主| 发表于 2018-1-19 11:11:32 | 显示全部楼层
我用下面的函数:
在FreeRTOS里运行一次系统就死了
FRESULT scan_files (char* path)
{
    FRESULT res;
    u32 i[8],j;
    char fn[64],tmppath[64];
    strcpy((char *)tmppath,(char *)path);
    for(j=0;j<8;j++) {
        res = f_opendir(&dir, (TCHAR*)tmppath);
        if (res == FR_OK) {
            i[j] = strlen(tmppath);
            {
                res = f_readdir(&dir, &fileinfo);
                if (res != FR_OK || fileinfo.fname[0] == 0) break;
                if (fileinfo.fname[0] == '.') continue;

                strcpy((char *)fn,*fileinfo.lfname ? fileinfo.lfname : fileinfo.fname);

                if (fileinfo.fattrib & AM_DIR) {//是目录
                    strcat(tmppath,"/");
                    strcat(tmppath,fn);
                    printf("%s\n", tmppath);
                    res = scan_files(tmppath);
                    if (res != FR_OK) break;
                    tmppath[i[j]] = 0;
                } else {
                    printf("%s/%s\n", tmppath, fn);
                }
            }
        }
    }

   return res;
}
脑子不够用,怎么改让它不死??

出0入0汤圆

 楼主| 发表于 2018-1-19 11:42:06 | 显示全部楼层
fn[64],tmppath[64]重复分配太多了造成溢出?

出0入0汤圆

发表于 2018-1-19 12:13:13 | 显示全部楼层
am3359 发表于 2018-1-19 11:42
fn[64],tmppath[64]重复分配太多了造成溢出?

是的  递归就怕这种问题

出0入0汤圆

 楼主| 发表于 2018-1-19 12:27:35 | 显示全部楼层
vuo50z 发表于 2018-1-19 10:33
上面那个函数每次递归都要占用256字节的buffer,改成下面这样,只在main中申请一个256字节就可以了。

vo ...

buffer打印完就没用了
但是作为路径子目录,回到父目录还需要buffer,不能完全丢弃?????

出0入0汤圆

发表于 2018-1-19 12:34:18 | 显示全部楼层
am3359 发表于 2018-1-19 12:27
buffer打印完就没用了
但是作为路径子目录,回到父目录还需要buffer,不能完全丢弃????? ...

我只是举个例子,具体到你的应用里要具体分析。

出0入0汤圆

 楼主| 发表于 2018-1-19 12:36:11 | 显示全部楼层
vuo50z 发表于 2018-1-19 12:34
我只是举个例子,具体到你的应用里要具体分析。

集思广益,用递归有办法解决栈的问题最好,这样可以拓展思路

出0入0汤圆

 楼主| 发表于 2018-1-19 12:38:35 | 显示全部楼层
本帖最后由 am3359 于 2018-1-19 12:49 编辑

[作者被禁言]

出0入0汤圆

发表于 2018-1-19 12:44:43 来自手机 | 显示全部楼层
am3359 发表于 2018-1-18 17:03
还差10分,就是中级会员了

目测id要不保了

出0入0汤圆

发表于 2018-1-19 13:05:49 来自手机 | 显示全部楼层
递归你也可以通过全局变量判断层数的嘛,或者分配内存不足的时候就赶紧结束递归不就好了

出0入0汤圆

 楼主| 发表于 2018-1-19 13:28:48 | 显示全部楼层
jiangkehong 发表于 2018-1-19 13:05
递归你也可以通过全局变量判断层数的嘛,或者分配内存不足的时候就赶紧结束递归不就好了 ...

会不会出现在一层内还没有查完所有的文件就发现内存要溢出了,直接退出,这样造成某一层读出的文件是不完整的

出0入0汤圆

 楼主| 发表于 2018-1-19 13:31:50 | 显示全部楼层
我觉得可以允许读到某层退出,但是不能允许某文件夹读了一部分退出

出0入0汤圆

发表于 2018-1-19 13:43:01 | 显示全部楼层
am3359 发表于 2018-1-19 13:28
会不会出现在一层内还没有查完所有的文件就发现内存要溢出了,直接退出,这样造成某一层读出的文件是不完 ...

堆栈是在进函数时调整的,所以只要进了这个函数就决定了是否溢出。

出0入0汤圆

 楼主| 发表于 2018-1-19 13:45:54 | 显示全部楼层
知道溢出肯定就不会调用这个函数了,文件目录应该认为是不可预知的树形分支

出0入0汤圆

发表于 2018-1-19 15:50:26 | 显示全部楼层
am3359 发表于 2018-1-19 13:45
知道溢出肯定就不会调用这个函数了,文件目录应该认为是不可预知的树形分支 ...

你在扫描目录的文件时候还要不停的申请内存?那你可能第一层都扫不完。应该是在进入文件夹的时候,要保存当前文件夹的信息,所以要重新申请内存。

出100入0汤圆

发表于 2018-1-20 17:24:13 | 显示全部楼层
am3359 发表于 2018-1-18 09:19
目录结构如下:                                遍历结果如下:
0:                                      ...

支持一下,感谢楼主努力

出100入101汤圆

发表于 2018-1-21 22:36:43 | 显示全部楼层
vuo50z 发表于 2018-1-19 09:04
递归占用堆栈多是因为所用函数不是所有内部变量都是需要保存的。想省堆栈就在顶层定义一个结构体,递归调用 ...

没怎么看懂。

出0入0汤圆

发表于 2018-1-22 12:53:39 来自手机 | 显示全部楼层
感谢楼主大大

出0入0汤圆

发表于 2018-1-22 13:06:34 | 显示全部楼层
不错,有机会时用用看。

出0入0汤圆

发表于 2018-4-2 11:11:52 | 显示全部楼层
实现方法不错,可以参考到自己的应用中,谢谢楼主分享
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 01:37

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表