c/c++跨平台实现新建删除文件夹(文件)及输出文件夹内所有文件名

最近要写一个新建和删除文件夹操作的控制代码,由于以前没怎么接触过系统层级的编程,因此对这方面了解很少。比较了解的也只有C语言中的FILE*、fopen;和C++中的fstream。但这也只能在已有的目录下新建文件

由于查资料时有很多代码都多多少少存在问题,因此我结合搜索到的有关解释和自己的测试,将解新建和删除文件夹(文件)的办法整合,且实现跨平台(Windows & Linux),希望对大家有帮助。

2022年3月补充:后来发现c++17的STL里包含了创建文件夹的库:std::filesystem::create_directory可以直接跨平台调用,无需调用后文所述的底层库^_^。https://en.cppreference.com/w/cpp/filesystem/create_directory

一、相关库函数及头文件

对文件夹的操作基本是和平台相关的!因此首先介绍一下各平台需要的库函数以及所在的头文件。内容有点多,不需要可直接看代码

1、功能:新建文件夹

Windows

形式:_mkdir(const char *_Path);

头文件:#include <direct.h>

输入参数:_Path=创建新文件夹的路径及文件夹名,如"E:\study\math",新建文件夹的路径为"E:\study",新建文件夹名为"math"

说明:若新建文件夹的路径存在,且路径下没有同名文件夹或文件,则可成功创建一个空文件夹,并返回0,否则返回-1。因此不能一次性创建多级目录!新建多级目录需要一级一级地新建。ps:Windows上的函数大多前面加了下划线。mkdir前面若不加下划线"_",vs上编译报错并给出“不赞成用POSIX名称"mkdir",应该用ISO c/c++名称“_mkdir””。具体原因由于本人才疏学浅也不知道,有牛人知道的欢迎评论交流~

Linux

形式:mkdir(const char *_Path, mode_t mode);

头文件:#include <sys/stat.h>

输入参数:_Path=新文件夹的路径和文件夹名,同_mkdir。mode=新建目录的权限一般设为0755,mode内容有点多,0755是从别的地方搬过来的,测试可用,详细内容在此不叙述。

说明:除了多了个输入参数,其余基本同Windows中的_mkdir


2、功能:删除文件夹

Windows

形式:_rmdir(const char *_Path);

头文件:#include <direct.h>

输入参数:_Path=需删除的空文件夹的路径及文件夹名,如“E:\study\math”,删除“E:\study”目录下,名为“math”的空文件夹。

说明:若输入参数路径存在,且被删除文件夹是一个空文件夹,则可成功删除该空文件夹,返回0,否则返回-1。因此要想删除某文件夹需要先删除文件夹内的文件和文件夹!代码部分会详述。

Linux

形式:rmdir(const char *_Path);

头文件:#include <unistd.h>

输入参数:_Path=需删除的空文件夹的路径及文件夹名,同_rmdir。

说明:主意头文件与mkdir不同


3、功能:检查文件夹(文件)的性质(主要检查是否存在)

Windows

形式:_access(const char *_FileName, int _AccessMode);

头文件:#include <io.h>

参数:_FileName=要检测的文件夹(文件)的路径,如“E:/study/math”,无论“math”是文件夹还是文件,都同样返回检测结果。_AccessMode=要检测的性质,主要赋0,检测是否存在。

说明:存在返回0,不存在返回-1。建立多级目录时可检测路径是否存在

Linux

形式:access(const char *_FileName, int _AccessMode);

头文件:#include <unistd.h>

参数:同_access

说明:同_access


4、功能:删除文件

Windows、Linux上的形式、用法都一样

形式:remove(const char *_FileName);

头文件:stdio.h(Windows和Linux)、io.h(仅Windows)

参数:_FileName=需要删除的文件路径和文件名

说明:该函数只能删除文件,不能删除文件夹,删除成功返回0,失败返回-1.


5. 功能:遍历文件夹中的文件

Windows通过正则化匹配

函数:_findfirst(const char *_FileName, _finddata_t* _FindData),_findnext(intptr_t handle, _finddata_t* _FindData),_findclose(intptr_t handle)

结构体:_finddata_t。

头文件:io.h

具体用法看代码:

string findPath = "E:/study/math/*";    //*号为正则化语法,表示附加任意字符的可匹配
struct _finddata_t fb;      //查找相同属性文件的存储结构体
intptr_t handle;            //正则化索引的句柄,用long类型会报错
handle = _findfirst(findPath.c_str(), &fb);    //匹配的第一个文件
if (handle != -1L)
{
    std::string pathTemp;
	do//循环找到的文件 
	{
	    //系统有个系统文件,名为“..”和“.”,对它不做处理  
		if (strcmp(fb.name, "..")!=0 && strcmp(fb.name, ".")!=0)//对系统隐藏文件的处理标记
		{
			// do something
		}
	} while (0 == _findnext(handle, &fb));//判断放前面会失去第一个搜索的结果
	//关闭文件夹,只有关闭了才能删除。找这个函数找了很久,标准c中用的是closedir  
	//经验介绍:一般产生Handle的函数执行后,都要进行关闭的动作。  
	_findclose(handle);
}

Linux通过打开文件夹读里面的文件:

函数:opendir(const char *_FileName),readdir(DIR* dir),closedir(DIR* dir)

结构体:DIR、dirent

头文件:dirent.h(有的地方说需要sys/type.h,但试了一下好像不需要)

具体用法看代码

string strPath = "E:/study/math/"
DIR *d = opendir(strPath.c_str());//打开这个目录
if (d != NULL)
{ 
	struct dirent *dt = NULL;
	while (dt = readdir(d))//逐个读取目录中的文件到dt
	{
		//系统有个系统文件,名为“..”和“.”,对它不做处理
		if (strcmp(dt->d_name, "..")!=0 && strcmp(dt->d_name, ".")!=0)
		{
			//do something
		}
	}
	closedir(d);
}

测试过Windows正则化匹配中,正反斜杠"\\"和“/”都是可以的,Linux中用"/"所以建议统一用“/”


6、查看给定路径是文件还是文件夹

在删除文件夹时,清楚文件夹内部信息时需要用到。

Windows要用_finddata_t结构体的attrib属性;

Linux则要用到<sys/stat.h>头文件中的结构体stat、函数stat(为什么同名还不太清楚)和S_ISDIR()宏。

具体用法看最后的代码。


二、程序实现

1、程序应注意的问题

1)新建文件夹

  1. 新建文件夹若文件夹已存在,或新建文件夹路径中有个同名文件,则会新建失败,新建文件夹函数已经可以对该情况作出检测,因此这对程序没有影响。如新建文件夹“E:/study/math”,但该文件夹已经存在,或“E:/study”中已存在文件“math”,则创建失败。
  2. 故只用注意是否是新建多级文件夹。因此新建文件夹时需要对路径一级一级判断,判断是否存在路径中的这些文件夹,若不存在则需要新建。如新建文件夹“E:/study/math”,E盘中没有“study”文件夹,需要先新建“E:/study”,再建“E:/study/math”。
  3. 由于判断函数无法分辨是文件夹还是文件,新建多级文件夹时,若存在同名文件,文件夹创建失败。如:新建文件夹“E:/study/math”,若“E:/”中有个文件名为“study”,则新建文件夹失败。

2)删除文件夹

  1. 删除文件夹本身就不存在,或者要删除的路径是个文件,删除失败,删除空文件夹的函数已经可以对该情况作出检测,因此这对程序没有影响。如删除“E:/study/math”,但并没有该文件夹,或者“math”是个文件,则删除失败。
  2. 删除文件夹存在,但是文件夹非空,也就是文件夹里面还有文件或者文件夹。对于文件,我们可以用库函数直接删除,如果是文件夹,则可递归调用删除文件夹的方式。如要删除文件夹“E:/study”,文件夹里有文件“score.txt”和文件夹“math”,文件“score.txt”直接remove删除,文件夹“math”则递归删除文件夹“E:/study/math”。

2、代码

新建和删除文件夹两个函数,编译测试通过

0)头文件和预处理

#include <stdio.h>
#include <string>
#include <string.h>
#include <iostream>
#ifdef _WIN32
#include <direct.h>		//for mkdir rmdir
#include <io.h>			//for access
#elif __linux__
#include <unistd.h>		//for mkdir rmdir
#include <sys/stat.h>	//for access
#include <dirent.h>		//for DIR remove
#endif

#ifdef _WIN32
#define ACCESS _access
#define MKDIR(a) _mkdir((a))
#define RMDIR(a) _rmdir((a))
#elif __linux__
#define ACCESS access
#define MKDIR(a) mkdir((a),0755)
#define RMDIR(a) rmdir((a))
#endif

1)新建文件夹

bool MkDir(const std::string& strPath)
{
	int i = 0;
	int nDirLen = strPath.length();
	if (nDirLen <= 0)
		return false;
	char *pDirTemp = new char[nDirLen + 4];
	strPath.copy(pDirTemp, nDirLen + 1, 0);// +1 to copy '\0'
	pDirTemp[nDirLen] = '\0';
	//在末尾加'/'
	if (pDirTemp[nDirLen - 1] != '\\' && pDirTemp[nDirLen - 1] != '/')
	{
		pDirTemp[nDirLen] = '/';
		pDirTemp[nDirLen + 1] = '\0';
		nDirLen++;
	}
	// 创建目录
	for (i = 0; i < nDirLen; i++)
	{
		if (pDirTemp[i] == '\\' || pDirTemp[i] == '/')
		{
			pDirTemp[i] = '\0';//截断后面的子目录,逐级查看目录是否存在,若不存在则创建
			//如果不存在,创建
			int statu;
			statu = ACCESS(pDirTemp, 0);
			if (statu != 0)//可能存在同名文件导致没有创建
			{
				statu = MKDIR(pDirTemp);
				if (statu != 0)//可能上级不是文件夹而是同名文件导致创建失败
				{
					return false;
				}
			}
			//支持linux,将所有\换成/
			pDirTemp[i] = '/';
		}
	}
	delete[] pDirTemp;
	return true;
}

2)删除文件夹

由于不大系统代码差异较大,代码通过编译指令分为Windows和Linux两个部分

bool RmDir(const std::string & path)
{
	std::string strPath = path;
#ifdef _WIN32
	struct _finddata_t fb;   //查找相同属性文件的存储结构体
	//制作用于正则化路径
	if (strPath.at(strPath.length() - 1) != '\\' || strPath.at(strPath.length() - 1) != '/')
		strPath.append("\\");
	std::string findPath = strPath + "*";
	intptr_t handle;//用long类型会报错
	handle = _findfirst(findPath.c_str(), &fb);
	//找到第一个匹配的文件
	if (handle != -1L)
	{
		std::string pathTemp;
		do//循环找到的文件 
		{
			//系统有个系统文件,名为“..”和“.”,对它不做处理  
			if (strcmp(fb.name, "..")!=0 && strcmp(fb.name, ".")!=0)//对系统隐藏文件的处理标记
			{
				//制作完整路径
				pathTemp.clear();
				pathTemp = strPath + std::string(fb.name);
				//属性值为16,则说明是文件夹,迭代  
				if (fb.attrib == _A_SUBDIR)//_A_SUBDIR=16
				{
					RmDir(pathTemp.c_str());
				}
				//非文件夹的文件,直接删除。对文件属性值的情况没做详细调查,可能还有其他情况。  
				else
				{
					remove(pathTemp.c_str());
				}
			}
		} while (0 == _findnext(handle, &fb));//判断放前面会失去第一个搜索的结果
		//关闭文件夹,只有关闭了才能删除。找这个函数找了很久,标准c中用的是closedir  
		//经验介绍:一般产生Handle的函数执行后,都要进行关闭的动作。  
		_findclose(handle);
	}
	//移除文件夹  
	return RMDIR(strPath.c_str())==0?true:false;

#elif __linux__
	if (strPath.at(strPath.length() - 1) != '\\' || strPath.at(strPath.length() - 1) != '/')
		strPath.append("/");
	DIR *d = opendir(strPath.c_str());//打开这个目录
	if (d != NULL)
	{ 
		struct dirent *dt = NULL;
		while (dt = readdir(d))//逐个读取目录中的文件到dt
		{
			//系统有个系统文件,名为“..”和“.”,对它不做处理
			if (strcmp(dt->d_name, "..")!=0 && strcmp(dt->d_name, ".")!=0)//判断是否为系统隐藏文件
			{
				struct stat st;//文件的信息
				std::string fileName;//文件夹中的文件名
				fileName = strPath + std::string(dt->d_name);
				stat(fileName.c_str(), &st);
				if (S_ISDIR(st.st_mode))
				{
					RmDir(fileName);
				}
				else
				{
					remove(fileName.c_str());
				}
			}
		}
		closedir(d);
	}
	return rmdir(strPath.c_str())==0?true:false;
#endif

}

全篇到此结束,全篇基本手打,如果觉得有点用可以点个赞~有什么看法欢迎讨论交流~

  • 13
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值