最“高效”的实现
Linux下C++可以通过调用系统的system函数来执行shell命令,这无疑是最快的实现方法,话不多说,上代码。
#include <stdlib.h>
#include <string>
void CopyFileAndDirectory(const std::string &_old_path, const std::string &_new_path)
{
std::string copy_command = "cp -r " + _old_path + " " + _new_path;
system(copy_command.c_str());
}
void RemoveFileAndDirectory(const std::string &_path)
{
std::string remove_command = "rm -rf " + _path;
system(remove_command.c_str());
}
没错,使用shell命令就是这么简单明了。
为什么会重复造轮子
在此之前我并不是很了解C++调用shell的操作,于是使用递归方式实现了一个版本(参见:Linux下C++实现目录的递归拷贝和删除),但是静态检查时MISRA规则要求不能使用递归。于是乎,我又开始思考怎么实现一个非递归的版本。
非递归方式的实现
实现细节在代码中详细注释了,偷了一下懒把两个主要接口的返回值类型都写成void了。
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <fstream>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <cstring>
/* Check whether the path identified by 'filename' is a file */
static bool IsFile(const std::string &filename)
{
struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0 && S_ISREG(buffer.st_mode));
}
/* Check whether the path identified by 'filefodler' is a fodler */
static bool IsDirectory(const std::string &filefodler)
{
struct stat buffer;
return (stat(filefodler.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode));
}
/* Copy the file named by old_name to new_name */
int CopyFile(const std::string &old_name, const std::string &new_name)
{
std::ifstream ifs(old_name, std::ifstream::binary);
std::ofstream ofs(new_name, std::ifstream::binary | std::ifstream::trunc);
if (ifs.good() == false)
{
return -1;
}
ofs << ifs.rdbuf();
ifs.close();
ofs.close();
return 0;
}
/* Copy all files and sub directories in directory to new path */
void CopyFileAndDirectory(const std::string &_old_path, const std::string &_new_path)
{
DIR *directory_ptr;
struct dirent *dirent_ptr;
/* Record the source directory and target directory. */
std::queue< std::pair<std::string, std::string>> path_queue;
path_queue.push(std::pair<std::string, std::string>(_old_path, _new_path));
while (path_queue.size() > 0)
{
/* Fetch the first element in the queue. */
auto path_pair = path_queue.front();
path_queue.pop();
/* If the element is a directory */
if (IsDirectory(path_pair.first))
{
/* Judge whether the target directory exists. */
if (access(path_pair.second.c_str(), 0) == -1)
{
/* If the target directory does not exist, create it */
if (mkdir(path_pair.second.c_str(), 0755) < 0)
{
std::cout << "[CopyFileAndDirectory] mkdir failed , the errno = " << strerror(errno) << std::endl;
}
}
/* Open the source directory */
if ((directory_ptr = opendir(path_pair.first.c_str())) == NULL)
{
std::cout << "open directory failed directory name = " << path_pair.first << std::endl;
}
/* Traverse source directory */
while ((dirent_ptr = readdir(directory_ptr)) != NULL)
{
if((strcmp(dirent_ptr->d_name, ".") != 0) && (strcmp(dirent_ptr->d_name, "..") != 0))
{
/* Add the elements under the source directory to the queue. */
auto old_file = path_pair.first + "/" + dirent_ptr->d_name;
auto new_file = path_pair.second + "/" + dirent_ptr->d_name;
path_queue.push(std::pair<std::string, std::string>(old_file, new_file));
}
}
closedir(directory_ptr);
}
else if (IsFile(path_pair.first))
{
/* If the element is a file, just copy it */
CopyFile(path_pair.first, path_pair.second);
}
else
{
/* do nothing */
}
}
}
/* Remove all files and sub directories in directory */
void RemoveFileAndDirectory(const std::string &_path)
{
DIR *directory_ptr;
struct dirent *dirent_ptr;
/* Record the directory name that will be removed */
std::queue<std::string> path_queue;
std::vector<std::string> path_set;
path_queue.push(_path);
while(path_queue.size() > 0)
{
/* Fetch the first element in the queue. */
auto path_name = path_queue.front();
path_queue.pop();
/* If the element is a directory */
if(IsDirectory(path_name))
{
/* Record the name of the directory and delete it after deleting its child elements. */
path_set.push_back(path_name);
/* Open the directory */
if ((directory_ptr = opendir(path_name.c_str())) == NULL)
{
std::cout << "Opendir error: " << strerror(errno) << std::endl;
}
/* Traverse the directory */
while ((dirent_ptr = readdir(directory_ptr)) != NULL)
{
if((strcmp(dirent_ptr->d_name, ".") != 0) && (strcmp(dirent_ptr->d_name, "..") != 0))
{
/* Add the elements under the directory which wiil be remove to the queue. */
auto item_path = path_name + "/" + dirent_ptr->d_name;
path_queue.push(item_path);
}
}
closedir(directory_ptr);
}
else if(IsFile(path_name))
{
/* If the element is a file, just remove it */
remove(path_name.c_str());
}
else
{
/* do nothing */
}
}
/* Remove empty directories */
while(path_set.size() > 0)
{
auto last_item = path_set.back();
path_set.pop_back();
auto result = rmdir(last_item.c_str());
if(result != 0)
{
std::cout<< "remove last_item failed = " << last_item << std::endl;
}
}
}
int main(void)
{
std::string old_path = "/WorkSpace/TestPath";
std::string new_path = "/WorkSpace/NewPath";
CopyFileAndDirectory(old_path, new_path);
RemoveFileAndDirectory(new_path);
return 0;
}