环境:windows10,VS2013
函数功能:删除指定目录下最后一次修改时间在两分钟之前的文件夹(这个时间可以自己设置)
功能具体描述:删除指定目录下的以“sid_”开头命名的满足特定条件的文件夹,对于该目录下其他的文件和文件夹不做任何操作。
对于以“sid_”开头命名的文件夹来说,满足什么样的条件才把它删除呢?就是当该文件夹下的所有文件(File)的最后一次修改时间都在两分钟之前,就把这个文件夹删除,如果有一个文件是在两分钟之内修改的,则不删除;如果该文件夹为空,不管是不是两分钟之内建立的,直接删除!
注意,我这里默认,“sid_”开头的文件夹下只有文件(File),不含有文件夹(Directory)!
代码如下。下面我首先来说说如何执行这个代码。
1. 在VS上新建一个空项目,将下面的代码复制上去,项目的名字我这里起的是叫做deleteFile;
2. 在代码的116行,将 char path[MAX_PATH] = "D:\\test\\deleteFile\\deleteFile"; 后面替换成一个任意的路径,待会就在这个路径下创建文件件,方便测试,注意这里都是双斜杠!
3. 有了上面两步,程序已经可以直接运行了,但是为了测试效果,还需要在刚才的路径下新建几个“sid_”开头命名的文件夹和文件,如下图:
我这里新建了三个文件夹sid_01、sid_03、sid_03和一个文件sid_04.txt,并且在sid_01和sid_02下都新建了一个文本文件,sid_03为空,这样是为了观察不同情况下的实验结果。
4. 等待两分钟(因为我这里设置的时间是两分钟),打开sid_01目录下的a.txt,在其中随便输入几个字符,保存。
运行程序,结果如下图:
sid_01由于其中的文件在两分钟之内做了修改,没有被删除,而sid_04.txt不是文件夹,也没有被删除,符合预期。
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys\stat.h>
#include <Windows.h>
#define snprintf(dest, size, format, ...) _snprintf_s(dest, size, (size) - 1, format, ## __VA_ARGS__)
#define SEP_CHAR "\\"
typedef WIN32_FIND_DATAA WIN32_FIND_DATA;
typedef struct _delName // 待删除的文件或者文件夹的名字
{
char name[100];
}delName;
// FileTimeToTime_t()
// 功能:将FILETIME类型的变量ft转化成time_t类型,并保存在指针t中
// 这段代码从网上copy的,随便一搜就搜到了
void FileTimeToTime_t(FILETIME ft, time_t *t)
{
LONGLONG ll;
ULARGE_INTEGER ui;
ui.LowPart = ft.dwLowDateTime;
ui.HighPart = ft.dwHighDateTime;
*t = ((LONGLONG)(ui.QuadPart - 116444736000000000) / 10000000);
}
// is_2mins_ago()
// 功能:判断入参的文件(File)是否是在2min中之内是否进行过写的操作,如果是就return TRUE,否则return FALSE
BOOL is_2mins_ago(WIN32_FIND_DATA *file)
{
time_t curTime;
time_t lastWriteTime;
time_t numSec = 2 * 60; // 2分钟包含 2*60 秒
time(&curTime);
FileTimeToTime_t((*file).ftLastWriteTime, &lastWriteTime);
if ((curTime - lastWriteTime) > numSec)
return TRUE;
return FALSE;
}
// isDelete()
// 功能:判断入参的文件夹(Directory)是否需要删除,如果是就return TRUE,否则return FALSE
// 参数:path指的是该文件夹所在的路径,dir就是该文件夹
BOOL isDelete(char* path, WIN32_FIND_DATA *dir)
{
WIN32_FIND_DATA p;
char buf[MAX_PATH] = { 0 };
snprintf(buf, sizeof(buf), "%s%s%s%s*.txt", path, SEP_CHAR, (*dir).cFileName, SEP_CHAR);
HANDLE h = FindFirstFile(buf, &p);
if (NULL == h) // 该文件夹为空是否删除?
{
return TRUE;
}
if (!is_2mins_ago(&p))
{
FindClose(h);
return FALSE;
}
while (FindNextFile(h, &p))
{
if (!is_2mins_ago(&p))
{
FindClose(h);
return FALSE;
}
}
FindClose(h);
return TRUE; // 该文件夹需要删除就return 1,否则return 0
}
// DeleteDirectory()
// 功能:删除path路径指定的文件夹,删除文件夹成功return TRUE,失败return FALSE
// 注:我这里默认该文件夹下只有文件,没有文件夹了,如果想删除带有文件夹的文件夹,请参考:https://blog.csdn.net/jaff20071234/article/details/6559533
BOOL DeleteDirectory(const char * path)
{
char buf[MAX_PATH] = { 0 };
snprintf(buf, MAX_PATH, "%s%s*.*", path, SEP_CHAR); //匹配格式为*.*,即该目录下的所有文件
WIN32_FIND_DATAA p;
ZeroMemory(&p, sizeof(WIN32_FIND_DATAA));
HANDLE h = FindFirstFile(buf, &p);
if (strcmp(p.cFileName, ".") && strcmp(p.cFileName, ".."))
{
snprintf(buf, sizeof(buf), "%s%s%s", path, SEP_CHAR, p.cFileName);
DeleteFile(buf);
}
while (FindNextFile(h, &p))
{
if (strcmp(p.cFileName, ".") && strcmp(p.cFileName, "..")) //如果不是"." ".."目录
{
snprintf(buf, sizeof(buf), "%s%s%s", path, SEP_CHAR, p.cFileName);
DeleteFile(buf);
}
}
FindClose(h); // 这一句不能放在该函数的最后,否则可能会导致后面的删除文件夹失败(你可以试试看)
BOOL bRet = RemoveDirectory(path);
if (bRet == 0) //删除目录
{
printf("delet directory faild!path: %s\n", path);
return FALSE;
}
return TRUE;
}
// delete_logDir_2mins_ago()
// 功能:对path这个变量指定目录下所有sid_开头的文件夹,如果该文件夹下没有文件,则删除;如果该文件夹下只要存在一个文件
// 是在当前时间的两分钟之内进行过写操作,则该文件夹保存,否则该将该文件夹删除
void delete_logDir_2mins_ago()
{
WIN32_FIND_DATA p;
int num = 0;
delName d[100];
char path[MAX_PATH] = "D:\\test\\deleteFile\\deleteFile"; // 这里的路径一定要用两个斜杠!
char buf[MAX_PATH] = { 0 };
snprintf(buf, sizeof(buf), "%s%ssid_*", path, SEP_CHAR);
HANDLE h = FindFirstFile(buf, &p);
if (NULL == h)
{
return;
}
if (p.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 判断是否为文件夹,参考:https://blog.csdn.net/c20081052/article/details/79912291
{
if (isDelete(path, &p))
{
snprintf(d[num].name, sizeof(d[num].name), "%s", p.cFileName);
num++;
}
}
while (FindNextFile(h, &p) && num<100)
{
if (p.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 判断是否为文件夹
{
if (isDelete(path, &p))
{
snprintf(d[num].name, sizeof(d[num].name), "%s", p.cFileName);
num++;
}
}
}
FindClose(h); // 这一句不能放在该函数的最后,否则可能会导致后面的删除文件夹失败(你可以试试看)
for (int j = 0; j < num; j++)
{
snprintf(buf, sizeof(buf), "%s%s%s", path, SEP_CHAR, d[j].name);
DeleteDirectory(buf);
}
}
int main()
{
delete_logDir_2mins_ago();
system("pause");
return 0;
}
在写代码的过程中发现以下几个值得注意或者学习的地方,顺便在此贴出来:
1. 代码中关闭由于打开句柄使用的是FindFirstFile()和FindNextFile()函数,应当使用FindClose()函数来关闭句柄,否则可能会存在句柄泄露的问题(不过我到现在还不明白句柄到底是个啥玩意)。
2. 我刚写完这个程序时,为了图方便,自己另外写了一个函数generateDir()用来自动生成图中的那些文件夹,然后在main函数中同时调用generateDir()和delete_logDir_2mins_ago(),可是在运行的时候却发现,一个文件/文件夹都没有被删除!最终发现,貌似无法在一个进程中生成文件/文件夹然后又把它删除,因为这些文件/文件夹貌似与这个进程关联起来了什么的。。。(我也不是很懂),所以最后还是每次自己手动创建文件夹来测试。其实也可以在main函数中每次注释掉一个函数,再执行另一个函数,就没问题了。
3. 关于为什么要把FILETIME类型转换成time_t类型?因为time_t类型是以秒为单位的,方便计算吧~目前据我了解Windows中有三个常用的时间类型time_t,FILETIME,SYSTEMTIME,有时间可以去学学,这三个类型的互相转换网上也有很多资料。
4. 在阅读Windows的程序时,经常会发现下面类似的宏定义:
#define FindFirstFile FindFirstFileA
一直不懂为什么要在函数名后面加个大写的A,经过搜索资料后,下图应该可以帮助理解: