将前缀和后缀相同的文件移动到同一个目录的算法设计及C代码实现

一、需求描述
在Linux系统的某几个目录下有一些前缀和后缀相同的文件,编写程序将它们移动到同一个目录下。

例如,有三个源目录FileDir1、FileDir2和FileDir3,里面分别存放有文件File_1.txt、File_2.txt和File_3.txt。由于它们有相同的前缀(File_)和后缀(txt),所以要将这三个文件移动到同一个结果目录(假设为GatherDir)中。

二、算法设计
基于需求,可以采用如图1所示的程序流程:

图1 程序总体流程

三、特殊流程考虑
在编写程序的过程中,对于某些特殊流程的考虑如下:
1.如果扫描某个目录出错,则直接停止程序的运行,而不用继续扫描下一个目录。

2.对于某些空文件(即文件的大小为0),直接在源目录中将其删除,而不用移动到结果目录中。

3.为了随时能够处理放到源目录中的文件,程序每隔一段时间(如一分钟)扫描一次源目录。也就是说,如果不人为操作,程序启动之后会不停地运行。

四、程序代码

/**********************************************************************
* 版权所有 (C)2016, Zhou Zhaoxiong。
*
* 文件名称:FileGather.c
* 文件标识:无
* 内容摘要:将各个目录中前缀相同的文件集中到一个目录中
* 其它说明:无
* 当前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20160513
*
**********************************************************************/
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <ftw.h>
#include <time.h>

// 重定义数据类型
typedef signed   int        INT32;
typedef unsigned int        UINT32;
typedef unsigned char       UINT8;

// 全局变量定义
UINT8  g_szGatherDir[256] = {0};     // 汇总文件的目录
UINT8  g_szFilePrefix[20] = {0};     // 需要汇总的文件的前缀
UINT8  g_szFileSuffix[20] = {0};     // 需要汇总的文件的后缀

// 宏定义
#define   DIRNUM     3               // 需要扫描的目录数

// 函数声明
INT32 SelectFlies(struct dirent *pDir);
void ScanDirAndGather(void);
void Sleep(UINT32 iCountMs);

/****************************************************************
* 功能描述: 主函数
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0-执行完成
* 其他说明: 无
* 修改日期       版本号        修改人        修改内容
* -------------------------------------------------------------
* 20160513        V1.0     Zhou Zhaoxiong     创建
****************************************************************/
INT32 main(void)
{
    // 获取汇总文件的目录
    snprintf(g_szGatherDir, sizeof(g_szGatherDir)-1, "%s/zhouzx/TestDir/GatherDir", getenv("HOME"));

    // 获取需要汇总的文件的前缀
    snprintf(g_szFilePrefix, sizeof(g_szFilePrefix)-1, "File_");

    // 获取需要汇总的文件的后缀
    snprintf(g_szFileSuffix, sizeof(g_szFileSuffix)-1, ".txt");

    // 调用函数执行文件的汇总
    while (1)
    {
        ScanDirAndGather();

        Sleep(60 * 1000);    // 每一分钟执行一次文件的汇总
    }

    return 0;
}

/**********************************************************************
* 功能描述:根据前缀和后缀选择文件
* 输入参数:dir-目录
* 输出参数:无
* 返 回 值:0-失败   1-成功
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20160513         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
INT32 SelectFlies(struct dirent *pDir)
{
    INT32 iPrefixLen    = 0;
    INT32 iLoopFlag     = 0;
    INT32 iSelectResult = 0;

    if (pDir == NULL)
    {
        printf("SelectFlies:input parameter is NULL!\n");
        return 0;
    }

    // 匹配文件前缀和后缀
    iPrefixLen = strlen(g_szFilePrefix);       // 前缀为g_szFilePrefix
    iSelectResult = ((0 == strncmp(pDir->d_name, g_szFilePrefix, iPrefixLen))
                     && ((strncmp(&pDir->d_name[strlen(pDir->d_name) - strlen(g_szFileSuffix)], g_szFileSuffix, strlen(g_szFileSuffix)) == 0)));

    if (iSelectResult == 1)            // 找到了匹配前缀的文件
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**********************************************************************
* 功能描述:扫描目录并汇总前缀相同的文件
* 输入参数:无
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20160513         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
void ScanDirAndGather(void)
{
    INT32  iScanDirRet           = 0;
    UINT32 iDirIdx               = 0;
    UINT32 iFileIdx              = 0;
    UINT32 iFileCount            = 0;
    UINT32 iScanedNoFileDirCount = 0;
    UINT32 iFileSize             = 0;
    INT32  iRetVal               = 0;
    UINT8  szFileDir[256]        = {0};
    UINT8  szScanedFile[512]     = {0};
    UINT8  szCmdBuf[256]         = {0};
    FILE  *fp                    = NULL;
    struct dirent **ppDirEnt     = NULL;

    // 依次扫描各个目录, 并汇总文件
    for (iDirIdx = 1; iDirIdx <= DIRNUM; iDirIdx ++)
    {
        memset(szFileDir, 0x00, sizeof(szFileDir));
        snprintf(szFileDir, sizeof(szFileDir)-1, "%s/zhouzx/TestDir/FileDir%d", getenv("HOME"), iDirIdx);

        iScanDirRet = scandir(szFileDir, &ppDirEnt, SelectFlies, alphasort);
        if (iScanDirRet < 0)   // 扫描目录出错
        {
            printf("ScanDirAndGather:exec scandir failed, path=%s\n", szFileDir);
            return;
        }
        else if (iScanDirRet == 0)   // 目录下无文件
        {
            printf("ScanDirAndGather:no satisfied file in directory %s\n", szFileDir);

            iScanedNoFileDirCount ++;
            if (iScanedNoFileDirCount >= DIRNUM)    // 表示所有目录下均无满足条件的文件
            {
                printf("ScanDirAndGather:scaned no satisfied files in all %d dirs\n", iScanedNoFileDirCount);
                return;
            }
        }
        else          // 将满足条件的文件移动到汇总目录中
        {
            for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++)
            {
                // 先判断扫描到的文件是否为空文件, 是则直接删除, 不是才执行移动的操作
                memset(szScanedFile, 0x00, sizeof(szScanedFile));
                snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s", szFileDir, ppDirEnt[iFileIdx]->d_name);
                fp = fopen(szScanedFile, "r");
                if (fp == NULL)          // 打开文件失败, 直接返回
                {
                    printf("ScanDirAndGather:open file %s failed, please check!\n", szScanedFile);
                    return;
                }
                fseek(fp, 0, SEEK_END);
                iFileSize = ftell(fp);
                if (iFileSize == 0)     // 该文件为空文件
                {
                    printf("ScanDirAndGather:%s is an empty file, so delete it directly!\n", szScanedFile);
                    memset(szCmdBuf, 0x00, sizeof(szCmdBuf));
                    snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "rm %s", szScanedFile);
                    system(szCmdBuf);
                }
                else
                {
                    memset(szCmdBuf, 0x00, sizeof(szCmdBuf));
                    snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s", szScanedFile, g_szGatherDir);
                    system(szCmdBuf);

                    printf("ScanDirAndGather:now, %s\n", szCmdBuf);

                    iFileCount ++;
                }
            }
        }
    }

    printf("ScanDirAndGather:this time,totally moved %d file(s) to %s\n", iFileCount, g_szGatherDir);

    return;
}

/**********************************************************************
* 功能描述: 程序休眠
* 输入参数: iCountMs-休眠时间(单位:ms)
* 输出参数: 无
* 返 回 值: 无
* 其它说明: 无
* 修改日期      版本号       修改人        修改内容
* ------------------------------------------------------------------
* 20160513       V1.0     Zhou Zhaoxiong     创建
********************************************************************/
void Sleep(UINT32 iCountMs)
{
    struct timeval t_timeout = {0};

    if (iCountMs < 1000)
    {
        t_timeout.tv_sec  = 0;
        t_timeout.tv_usec = iCountMs * 1000;
    }
    else
    {
        t_timeout.tv_sec  = iCountMs / 1000;
        t_timeout.tv_usec = (iCountMs % 1000) * 1000;
    }
    select(0, NULL, NULL, NULL, &t_timeout);    // 调用select函数阻塞程序
}

五、程序测试
将编写好的程序“FileGather.c”上传到Linux机器,并使用“gcc -g -o FileGather FileGather.c”命令对该程序进行编译,生成“FileGather”文件。下面对程序进行详细的测试。

1.在启动程序之前,分别在源目录FileDir1、FileDir2和FileDir3中放入文件File_1.txt、File_2.txt和File_3.txt,程序运行情况如下:

ScanDirAndGather:now, mv /home/zhou/zhouzx/TestDir/FileDir1/File_1.txt /home/zhou/zhouzx/TestDir/GatherDir
ScanDirAndGather:now, mv /home/zhou/zhouzx/TestDir/FileDir2/File_2.txt /home/zhou/zhouzx/TestDir/GatherDir
ScanDirAndGather:now, mv /home/zhou/zhouzx/TestDir/FileDir3/File_3.txt /home/zhou/zhouzx/TestDir/GatherDir
ScanDirAndGather:this time,totally moved 3 file(s) to /home/zhou/zhouzx/TestDir/GatherDir

可以看到,源目录中的三个文件已经没有了,它们被移动到了结果目录GatherDir中:

~/zhouzx/TestDir/GatherDir> ll
-rw——- 1 zhou users 12 2016-05-13 15:14 File_1.txt
-rw——- 1 zhou users 12 2016-05-13 15:14 File_2.txt
-rw——- 1 zhou users 12 2016-05-13 15:14 File_3.txt

2.一段时间之后,在源目录FileDir1中放入文件File1_4.txt,程序运行情况如下:

ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir1
ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir2
ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir3
ScanDirAndGather:scaned no satisfied files in all 3 dirs

可以看到,因为前缀不匹配,File1_4.txt文件仍然在源目录FileDir1中:

~/zhouzx/TestDir/FileDir1> ll
-rw——- 1 zhou users 36 2016-05-13 15:19 File1_4.txt

3.一段时间之后,在源目录FileDir2中放入文件File_5.txt,在源目录FileDir3中放入文件File_11.c,程序运行情况如下:

ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir1
ScanDirAndGather:now, mv /home/zhou/zhouzx/TestDir/FileDir2/File_5.txt /home/zhou/zhouzx/TestDir/GatherDir
ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir3
ScanDirAndGather:this time,totally moved 1 file(s) to /home/zhou/zhouzx/TestDir/GatherDir

可以看到,源目录FileDir2中的文件File_5.txt已经没有了,因为后缀不匹配,源目录FileDir3中的文件File_11.c还存在:

~/zhouzx/TestDir/FileDir3> ll
-rw——- 1 zhou users 4 2016-05-13 15:23 File_11.c

File_5.txt已经被移动到了结果目录GatherDir中:

~/zhouzx/TestDir/GatherDir> ll
-rw——- 1 zhou users 12 2016-05-13 15:14 File_1.txt
-rw——- 1 zhou users 12 2016-05-13 15:14 File_2.txt
-rw——- 1 zhou users 12 2016-05-13 15:14 File_3.txt
-rw——- 1 zhou users 36 2016-05-13 15:23 File_5.txt

4.一段时间之后,在源目录FileDir2中放入空文件File_7.txt,程序运行情况如下:

ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir1
ScanDirAndGather:/home/zhou/zhouzx/TestDir/FileDir2/File_7.txt is an empty file, so delete it directly!
ScanDirAndGather:no satisfied file in directory /home/zhou/zhouzx/TestDir/FileDir3
ScanDirAndGather:this time,totally moved 0 file(s) to /home/zhou/zhouzx/TestDir/GatherDir

可以看到,源目录FileDir2中的文件File_7.txt已经被删除掉了:

~/zhouzx/TestDir/FileDir2> ll
total 0

六、需求扩展
基于本文中的需求和程序,可考虑对需求进行以下扩展:
1.在移动文件之前,先查看相同文件名的文件在结果目录中是否存在,如果存在,则直接将该文件在源目录中删除掉;如果不存在,才将该文件移动到结果目录中。

2.为避免结果目录中的文件过多,可以在程序中添加清理机制,即将存放时间超过一定时长的文件删除掉。

3.为了体现程序的灵活性,可将部分文件信息(如文件前缀、后缀、存放目录、扫描间隔时长等)存放到配置文件中,程序在启动时读取相关的配置项的值来执行后续目录扫描和文件移动的操作。

时间: 2024-09-04 19:29:13

将前缀和后缀相同的文件移动到同一个目录的算法设计及C代码实现的相关文章

将源目录中的文件按照前缀分发到不同目录中的算法设计及C代码实现

一.需求描述 在Linux系统的某个源目录中有一批后缀相同的文件,编写程序将这些文件按照前缀分发到不同的目录中. 例如,源目录SourceDir中存放有三个后缀相同的文件File1_1.txt.File2_1.txt和File3_1.txt,按照前缀File1_.File2_和File3_将它们分别移动(分发)到目录FileDir1.FileDir2和FileDir3中. 二.算法设计 基于需求,可以采用如图1所示的程序流程: 图1 程序总体流程 三.特殊流程考虑 在编写程序的过程中,对于某些特

Linux下文件分发的算法设计及C代码实现

需求描述 在Linux系统的某个源目录中有一批后缀相同的文件,编写程序将这些文件按照前缀分发到不同的目录中. 例如,源目录SourceDir中存放有三个后缀相同的文件File1_1.txt.File2_1.txt和File3_1.txt,按照前缀File1_.File2_和File3_将它们分别移动(分发)到目录FileDir1.FileDir2和FileDir3中. 算法设计 基于需求,可以采用如图1所示的程序流程: 图1 程序总体流程 特殊流程考虑 在编写程序的过程中,对于某些特殊流程的考虑

【SpringMVC框架】小结+视图解析器配置前缀和后缀

1.入门程序小结 通过入门程序理解springmvc前端控制器.处理器映射器.处理器适配器.视图解析器用法. 前端控制器配置: 第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析 第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析   使用此种方式可以实现 RESTful风格的url 处理器映射器: 非注解处理器映射器(了解) 注解的处理器映射器(掌握)对

如何为Excel批量加前缀或后缀

  在使用Excel表格工作时,我们很经常接触输入相同的前缀或者后缀的情况.这些前缀虽然简单,但逐个输入起来确实繁琐而枯燥,增加了工作量.这里和大家一起分享一个Excel技巧,让单元格在输入时自动添加前缀,或者快速修改增加原有文档的前缀. 操作步骤 方法一:修改单元格格式.选择需要添前缀的单元格,打开"单元格格式"对话框,选择"数字"选项卡下的"自定义". 在右边的"类型"下输入 "前缀@",如我要加的前缀

c++-哪些情况必须使用指定字面值类型的前缀和后缀?

问题描述 哪些情况必须使用指定字面值类型的前缀和后缀? <C++ Primer>第五版,中文版.p37. 请输入长度为30-10000的问题描述 解决方案 类型不确定的时候,比如 auto i = 1; //推断为int 要推断为float必须 auto i = 1F; 解决方案二: C++ 指定字面值的类型

Asp.net 文件上传类(取得文件后缀名,保存文件,加入文字水印)_实用技巧

复制代码 代码如下: using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; usi

JavaScript判断前缀、后缀是否是空格的方法_javascript技巧

本文实例讲述了JavaScript判断前缀.后缀是否是空格的方法.分享给大家供大家参考.具体如下: // Js 判断后缀 String.prototype.endsWith = function(suffix) { return this.indexOf(suffix,this.length - suffix.length)!==-1; }; // Js 判断前缀 if (typeof String.prototype.startsWith != 'function') { // see bel

c-MIC包含了offload.h文件之后,会出现很多问题,无论测试代码是什么,都会出现以下问题:

问题描述 MIC包含了offload.h文件之后,会出现很多问题,无论测试代码是什么,都会出现以下问题: c:program files (x86)intelcomposer xe 2013 sp1compilerincludeoffload.h(441): warning C4346: "__offload::shared_allocator::pointer": 依赖名称不是类型 1> 用"typename"为前缀来表示类型 1>c:program

Android 遍历SDCARD的文件夹并显示目录信息

Android 遍历SDCARD的文件夹并显示目录信息 private String mResult = new String(); private String[] mFileList = null; 1).显示所有文件/ File flist = new File("/mnt/sdcard"); mFileList = flist.list(); for(String str: mFileList){ mResult += str; mResult += "\n"