將前綴和后綴相同的文件移動(dòng)到同一個(gè)目錄的算法設(shè)計(jì)及C代碼實(shí)現(xiàn)
一、需求描述
在Linux系統(tǒng)的某幾個(gè)目錄下有一些前綴和后綴相同的文件,編寫(xiě)程序?qū)⑺鼈円苿?dòng)到同一個(gè)目錄下。
例如,有三個(gè)源目錄FileDir1、FileDir2和FileDir3,里面分別存放有文件File_1.txt、File_2.txt和File_3.txt。由于它們有相同的前綴(File_)和后綴(txt),所以要將這三個(gè)文件移動(dòng)到同一個(gè)結(jié)果目錄(假設(shè)為GatherDir)中。
二、算法設(shè)計(jì)
基于需求,可以采用如圖1所示的程序流程:
圖1 程序總體流程
三、特殊流程考慮
在編寫(xiě)程序的過(guò)程中,對(duì)于某些特殊流程的考慮如下:
1.如果掃描某個(gè)目錄出錯(cuò),則直接停止程序的運(yùn)行,而不用繼續(xù)掃描下一個(gè)目錄。
2.對(duì)于某些空文件(即文件的大小為0),直接在源目錄中將其刪除,而不用移動(dòng)到結(jié)果目錄中。
3.為了隨時(shí)能夠處理放到源目錄中的文件,程序每隔一段時(shí)間(如一分鐘)掃描一次源目錄。也就是說(shuō),如果不人為操作,程序啟動(dòng)之后會(huì)不停地運(yùn)行。
四、程序代碼
- /**********************************************************************
- * 版權(quán)所有 (C)2016, Zhou Zhaoxiong。
- *
- * 文件名稱(chēng):FileGather.c
- * 文件標(biāo)識(shí):無(wú)
- * 內(nèi)容摘要:將各個(gè)目錄中前綴相同的文件集中到一個(gè)目錄中
- * 其它說(shuō)明:無(wú)
- * 當(dāng)前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20160513
- *
- **********************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <dirent.h>
- #include <ftw.h>
- #include <time.h>
- // 重定義數(shù)據(jù)類(lèi)型
- 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 // 需要掃描的目錄數(shù)
- // 函數(shù)聲明
- INT32 SelectFlies(struct dirent *pDir);
- void ScanDirAndGather(void);
- void Sleep(UINT32 iCountMs);
- /****************************************************************
- * 功能描述: 主函數(shù)
- * 輸入?yún)?shù): 無(wú)
- * 輸出參數(shù): 無(wú)
- * 返 回 值: 0-執(zhí)行完成
- * 其他說(shuō)明: 無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- * -------------------------------------------------------------
- * 20160513 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ****************************************************************/
- 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");
- // 調(diào)用函數(shù)執(zhí)行文件的匯總
- while (1)
- {
- ScanDirAndGather();
- Sleep(60 * 1000); // 每一分鐘執(zhí)行一次文件的匯總
- }
- return 0;
- }
- /**********************************************************************
- * 功能描述:根據(jù)前綴和后綴選擇文件
- * 輸入?yún)?shù):dir-目錄
- * 輸出參數(shù):無(wú)
- * 返 回 值:0-失敗 1-成功
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- * --------------------------------------------------------------------
- * 20160513 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- 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;
- }
- }
- /**********************************************************************
- * 功能描述:掃描目錄并匯總前綴相同的文件
- * 輸入?yún)?shù):無(wú)
- * 輸出參數(shù):無(wú)
- * 返 回 值:無(wú)
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- * --------------------------------------------------------------------
- * 20160513 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- 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;
- // 依次掃描各個(gè)目錄, 并匯總文件
- 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) // 掃描目錄出錯(cuò)
- {
- printf("ScanDirAndGather:exec scandir failed, path=%s\n", szFileDir);
- return;
- }
- else if (iScanDirRet == 0) // 目錄下無(wú)文件
- {
- printf("ScanDirAndGather:no satisfied file in directory %s\n", szFileDir);
- iScanedNoFileDirCount ++;
- if (iScanedNoFileDirCount >= DIRNUM) // 表示所有目錄下均無(wú)滿足條件的文件
- {
- printf("ScanDirAndGather:scaned no satisfied files in all %d dirs\n", iScanedNoFileDirCount);
- return;
- }
- }
- else // 將滿足條件的文件移動(dòng)到匯總目錄中
- {
- for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++)
- {
- // 先判斷掃描到的文件是否為空文件, 是則直接刪除, 不是才執(zhí)行移動(dòng)的操作
- memset(szScanedFile, 0x00, sizeof(szScanedFile));
- snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s", szFileDir, ppDirEnt[iFileIdx]->d_name);
- fp = fopen(szScanedFile, "r");
- if (fp == NULL) // 打開(kāi)文件失敗, 直接返回
- {
- 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;
- }
- /**********************************************************************
- * 功能描述: 程序休眠
- * 輸入?yún)?shù): iCountMs-休眠時(shí)間(單位:ms)
- * 輸出參數(shù): 無(wú)
- * 返 回 值: 無(wú)
- * 其它說(shuō)明: 無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- * ------------------------------------------------------------------
- * 20160513 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ********************************************************************/
- 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); // 調(diào)用select函數(shù)阻塞程序
- }
五、程序測(cè)試
將編寫(xiě)好的程序“FileGather.c”上傳到Linux機(jī)器,并使用“gcc -g -o FileGather FileGather.c”命令對(duì)該程序進(jìn)行編譯,生成“FileGather”文件。下面對(duì)程序進(jìn)行詳細(xì)的測(cè)試。
1.在啟動(dòng)程序之前,分別在源目錄FileDir1、FileDir2和FileDir3中放入文件File_1.txt、File_2.txt和File_3.txt,程序運(yùn)行情況如下:
- 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
可以看到,源目錄中的三個(gè)文件已經(jīng)沒(méi)有了,它們被移動(dòng)到了結(jié)果目錄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.一段時(shí)間之后,在源目錄FileDir1中放入文件File1_4.txt,程序運(yùn)行情況如下:
- 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
可以看到,因?yàn)榍熬Y不匹配,F(xiàn)ile1_4.txt文件仍然在源目錄FileDir1中:
- ~/zhouzx/TestDir/FileDir1> ll
- -rw——- 1 zhou users 36 2016-05-13 15:19 File1_4.txt
3.一段時(shí)間之后,在源目錄FileDir2中放入文件File_5.txt,在源目錄FileDir3中放入文件File_11.c,程序運(yùn)行情況如下:
- 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已經(jīng)沒(méi)有了,因?yàn)楹缶Y不匹配,源目錄FileDir3中的文件File_11.c還存在:
- ~/zhouzx/TestDir/FileDir3> ll
- -rw——- 1 zhou users 4 2016-05-13 15:23 File_11.c
File_5.txt已經(jīng)被移動(dòng)到了結(jié)果目錄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.一段時(shí)間之后,在源目錄FileDir2中放入空文件File_7.txt,程序運(yùn)行情況如下:
- 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已經(jīng)被刪除掉了:
- ~/zhouzx/TestDir/FileDir2> ll
- total 0
六、需求擴(kuò)展
基于本文中的需求和程序,可考慮對(duì)需求進(jìn)行以下擴(kuò)展:
1.在移動(dòng)文件之前,先查看相同文件名的文件在結(jié)果目錄中是否存在,如果存在,則直接將該文件在源目錄中刪除掉;如果不存在,才將該文件移動(dòng)到結(jié)果目錄中。
2.為避免結(jié)果目錄中的文件過(guò)多,可以在程序中添加清理機(jī)制,即將存放時(shí)間超過(guò)一定時(shí)長(zhǎng)的文件刪除掉。
3.為了體現(xiàn)程序的靈活性,可將部分文件信息(如文件前綴、后綴、存放目錄、掃描間隔時(shí)長(zhǎng)等)存放到配置文件中,程序在啟動(dòng)時(shí)讀取相關(guān)的配置項(xiàng)的值來(lái)執(zhí)行后續(xù)目錄掃描和文件移動(dòng)的操作。
【本文是51CTO專(zhuān)欄作者周兆熊的原創(chuàng)文章,作者微信公眾號(hào):周氏邏輯(logiczhou)】