算法执行效率有高有底,数据结构设计有好有坏,代码质量亦可分出优劣,不管哪一个环节的质量好坏,都会影响呈现到用户面前的最终体验。玩过3D网游的童鞋都知道,暴雪的魔兽世界场景优美、画面流畅,加载时间还短、不吃内存(唯一“不足”是角色丑陋),国产3D就逊多了。所以,除了架构设计上有优有劣、算法上存在高明与否,编码上也是有得考究的。实现同样的功能,不同的人写的代码是可以看到差别的,同一个人随着经验的积累也会写出越来越好的代码。以常用到的一个“删除文件夹”功能函数为例,具体看看差别都在哪里。好了,不啰嗦了,且看代码。
第一次用这个功能,是在网上随便索罗了一个版本,略微修改也就采用了:
#include <direct.h>
#include <io.h>
void EA_vDeleteDirectory(const char *DirName)
{
char acFileUrl[260];
char acTempDir[260];
char next_path[260];
struct _finddata_t fileinfo;
int findlen;
int hFile;
memset(acFileUrl, 0x00, sizeof(acFileUrl));
memset(acTempDir, 0x00, sizeof(acTempDir));
sprintf(acFileUrl, "%s", DirName);
if( acFileUrl[strlen(acFileUrl) - 1] != '\\' && acFileUrl[strlen(acFileUrl) - 1] != '/')
{
strcat(acFileUrl, "/");
}
strcpy(acTempDir, acFileUrl);
strcat(acFileUrl, "*.*");
findlen = _findfirst(acFileUrl, &fileinfo);
if(findlen == -1)
{
return;
}
hFile = findlen;
while (findlen != -1)
{
if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
{
// 跳过. 和 ..
findlen = _findnext(hFile, &fileinfo);
continue;
}
if (fileinfo.attrib == _A_SUBDIR)
{
// 删除子目录
memset(next_path, 0x00, sizeof(next_path));
sprintf(next_path, "%s%s", acTempDir, fileinfo.name);
EA_vDeleteDirectory(next_path);
}
else
{
memset(next_path, 0, sizeof(next_path));
sprintf(next_path, "%s%s", acTempDir, fileinfo.name);
remove(next_path);
}
findlen = _findnext(hFile, &fileinfo);
}
_findclose( hFile );
rmdir(DirName);
return;
}
可以看到,每一层递归都会在堆栈上开辟3个260字节长度的临时空间。
后来觉得没必要使用这么多临时变量,第二次使用这个功能时略微进行了改进(Linux C版本):
#include <unistd.h>
#include <dirent.h>
void EA_PUB_vDeleteDirectory(const char *pcDirName)
{
if(pcDirName == NULL || strlen(pcDirName) == 0)
{
return;
}
DIR *dir;
struct dirent *entry;
char acNextPath[MAX_PATH];
dir = opendir(pcDirName);
if (dir == NULL)
{
return;
}
while ((entry = readdir(dir)) != NULL)
{
if(strcmp(entry->d_name ,"." ) ==0 || strcmp(entry->d_name ,".." ) ==0 )
{
continue;
}
if (entry->d_type == DT_DIR)
{
sprintf(acNextPath, "%s/%s", pcDirName, entry->d_name);
EA_PUB_vDeleteDirectory(acNextPath);
}
else
{
sprintf(acNextPath, "%s/%s", pcDirName, entry->d_name);
remove(acNextPath);
}
}
closedir(dir);
remove(pcDirName);
}
这样修改之后,每层递归只需要分配一个MAX_PATH长度的临时空间,于是便心安理得地使用起来。
再后来整理一些基本的功能函数,考虑做成库,希望能够达成“一劳永逸”之效。出于这种考虑便是愿意花费些功夫对里面的每个功能函数细心编码,虽不求完美,但仍然期望在一定范围内做到最好。再次认真审视DeleteDirectory这个函数的时候,可能缘于曾经有接触过Linux下ftw函数的原因,突然来了灵感,最后版本的“递归删除文件夹”函数实现如下:
//删除目录,可能删除失败,对内使用,对外使用的话请选择lib_vDeleteDir
static void DelDirectory(char *pcDirName)
{
char *pcEnd = NULL;
struct _finddata_t fileinfo;
int iFindStart = 0;
int iFind = 0;
pcEnd = pcDirName + strlen(pcDirName) - 1;
if( *pcEnd != '/' && *pcEnd != '\\')
{
*(++pcEnd) = '/';
}
*(++pcEnd) = '\0';
strcpy(pcEnd, "*.*");
iFindStart = _findfirst(pcDirName, &fileinfo);
if(iFindStart == -1)
{
return;
}
iFind = iFindStart;
while (iFind != -1)
{
if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
{
// 跳过. 和 ..
iFind = _findnext(iFindStart, &fileinfo);
continue;
}
if (fileinfo.attrib == _A_SUBDIR)
{
// 删除子目录
strcpy(pcEnd, fileinfo.name);
DelDirectory(pcDirName);
}
else
{
strcpy(pcEnd, fileinfo.name);
remove(pcDirName);
}
iFind = _findnext(iFindStart, &fileinfo);
}
_findclose( iFindStart );
*pcEnd = '\0';
rmdir(pcDirName);
return;
}
//删除整个目录
void lib_vDeleteDir(const char *pcDirName)
{
char acDir[LEN_1K];
if(pcDirName == NULL || *pcDirName == '\0')
{
return;
}
strcpy(acDir, pcDirName);
DelDirectory(acDir);
}
只需要一次性开辟1024字节长度的存储开销,在基于共同的上一级目录下进行路径的拼接,比较好地实现了同样的功能。
至此,前后3个版本的代码已悉数贴出。
我又在想,越是高级的语言,就越是不需要考虑太多细节,某个功能实现起来就越是省事。如果用Java语言来实现这个功能,基本上会这样写吧:
public static void deleteFile(File file) {
if(file != null) {
if (file.isFile()) {
file.delete();
} else if (file.isDirectory()) {
for(File f : file.listFiles()) {
deleteFile(f);
}
file.delete();
}
}
}
public static void emptyDir(File dir) {
if(dir!=null && dir.isDirectory()) {
for (File file : dir.listFiles()) {
deleteFile(file);
}
}
}