最近因为测试目的需要遍历一个目录下面的所有文件进行操作,主要是读每个文件的内容,只要知道文件名就OK了。在Java中直接用File类就可以搞定,因为Java中使用了组合模式,使得客户端对单个文件和文件夹的使用具有一致性,非常方便。但在C中就不一样了,而且在不同的平台下使用方法也不同。在Linux下实现该功能就非常方便,因为自带有API库,几个函数用起来得心应手(虽然有些小问题,后面说),在Windows下实现就不是那么方便,虽然也有自己的API,但用法有些晦涩难懂,因为没有封装起来,需要自己一步一步进行操作,因为用的是Windows API库函数所以如果对Windows编程不熟悉的话,照搬网上的代码错了也不易调试。为此,我把这些操作都封装成类似Linux下的库函数,一方面简化透明了操作,另一方面(也许更重要)就是移植性,这样将包含该功能的程序从Windows上移植到Linux下就无需改动代码了(删掉实现封装的文件,因为Linux下自带了),当然从Linux下移植到Windows下同样方便(增加实现封装的文件即可),这就是所谓的OCP原则吧(开放封闭原则,具体见:程序员该有的艺术气质—SOLID原则)。好了,首先看下Linux下是如何实现这个功能的。
一、Linux下实现目录操作的API函数都在头文件dirent.h中,截取部分该文件内容如下:
/** structure describing an open directory. */
typedef struct _dirdesc {
int dd_fd; /** file descriptor associated with directory */
long dd_loc; /** offset in current buffer */
long dd_size; /** amount of data returned by getdirentries */
char *dd_buf; /** data buffer */
int dd_len; /** size of data buffer */
long dd_seek; /** magic cookie returned by getdirentries */
long dd_rewind; /** magic cookie for rewinding */
int dd_flags; /** flags for readdir */
struct pthread_mutex *dd_lock; /** lock */
struct _telldir *dd_td; /** telldir position recording */
} DIR;
typedef void * DIR;
DIR *opendir(const char *);
DIR *fdopendir(int);
struct dirent *readdir(DIR *);
void seekdir(DIR *, long);
long telldir(DIR *);
void rewinddir(DIR *);
int closedir(DIR *);
struct dirent
{
long d_ino; /* inode number*/
off_t d_off; /* offset to this dirent*/
unsigned short d_reclen; /* length of this d_name*/
unsigned char d_type; /* the type of d_name*/
char d_name[1]; /* file name (null-terminated)*/
};
测试 main.c
#include
#include
#include
#include
#define MAX_LEN 65535
int main(void)
{
DIR *dir;
struct dirent *ptr;
char *flow[MAX_LEN]; //指针数组 每个元素是指向文件名的指针
int num = 0, i = 0;
if ((dir=opendir("./data")) == NULL)
{
perror("Open dir error...");
exit(1);
}
// readdir() return next enter point of directory dir
while ((ptr=readdir(dir)) != NULL)
{
flow[num++] = ptr->d_name;
// printf("%s\n", flow[num - 1]);
//flow[num] = (char*)malloc(sizeof(char));
//strcpy(flow[num], ptr->d_name);
//num++;
}
for(i = 0; i < num; i++)
{
printf("%s\n", flow[i]);
}
closedir(dir);
}
二、Windows 下遍历目录的方法
1. 首先模拟Linux下自带的头文件dirent.h
// dirent.h
#ifndef _SYS_DIRENT_H
#define _SYS_DIRENT_H
typedef struct _dirdesc {
int dd_fd; /** file descriptor associated with directory */
long dd_loc; /** offset in current buffer */
long dd_size; /** amount of data returned by getdirentries */
char *dd_buf; /** data buffer */
int dd_len; /** size of data buffer */
long dd_seek; /** magic cookie returned by getdirentries */
} DIR;
# define __dirfd(dp) ((dp)->dd_fd)
DIR *opendir(const char *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
int closedir(DIR *);
#include
struct dirent
{
long d_ino; /* inode number*/
off_t d_off; /* offset to this dirent*/
unsigned short d_reclen; /* length of this d_name*/
unsigned char d_type; /* the type of d_name*/
char d_name[1]; /* file name (null-terminated)*/
};
#endif
2、dirent.cpp
// dirent.c
#include
#include
#include "dirent.h"
static HANDLE hFind;
DIR *opendir(const char *name)
{
DIR *dir;
WIN32_FIND_DATAA FindData;
char namebuf[512];
sprintf(namebuf, "%s\\*.*", name);
printf("%s\n", namebuf);
//hFind = FindFirstFile((LPCWSTR)namebuf, &FindData); // 该函数调试不行
hFind = FindFirstFileA((LPCSTR)namebuf, &FindData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf("FindFirstFile failed (%d)\n", GetLastError());
system("pause");
return 0;
}
dir = (DIR *)malloc(sizeof(DIR));
if (!dir)
{
printf("DIR memory allocate fail\n");
return 0;
}
memset(dir, 0, sizeof(DIR));
dir->dd_fd = 0; // simulate return
return dir;
}
struct dirent *readdir(DIR *d)
{
int i;
static struct dirent dirent;
BOOL bf;
WIN32_FIND_DATA FileData;
if (!d)
{
return 0;
}
bf = FindNextFile(hFind, &FileData);
//fail or end
if (!bf)
{
return 0;
}
for (i = 0; i < 256; i++)
{
dirent.d_name[i] = FileData.cFileName[i];
if (FileData.cFileName[i] == '\0') break;
}
dirent.d_reclen = i;
dirent.d_reclen = FileData.nFileSizeLow;
//check there is file or directory
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
dirent.d_type = 2;
}
else
{
dirent.d_type = 1;
}
return (&dirent);
}
int closedir(DIR *d)
{
if (!d) return -1;
hFind = 0;
free(d);
return 0;
}
main.cc
#include "dirent.h"
#include "timestamp.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX_LEN 65535
int main(void)
{
DIR *dir;
struct dirent *dir_item;
char *flow[MAX_LEN];
int num = 0, i = 0;
string logpath_ = ".\\log";
//static const int log_life = 30 * 24 * 3600;
static const int log_life = 2*60; // 1 min
printf("before opendir\n");
if ((dir = opendir(logpath_.c_str())) == NULL)
{
perror("Open dir error...");
system("pause");
exit(1);
}
printf("before readdir\n");
// readdir() return next enter point of directory dir
while ((dir_item = readdir(dir)) != NULL)
{
//flow[num++] = ptr->d_name;
//strcpy(flow[num++], ptr->d_name);
//printf("%s\n", flow[num - 1]);
//printf("num: %d\n", num);
if (strstr(dir_item->d_name, ".log") == NULL)
{
continue;
}
char file[128] = { 0 };
sprintf(file, "%s\\%s", logpath_.c_str(), dir_item->d_name);
printf("file: %s\n", file);
//system("pause");
struct _stat file_attr;
int ret = _stat(file, &file_attr);
if (ret < 0)
{
printf("stat '%s' failed.\n", file);
continue;
}
if ((file_attr.st_mode & S_IFMT) != S_IFREG)
{
printf("S_IFREG\n");
continue;
}
printf("second = %d\n", TimeStamp::now().Seconds());
printf("st_mtime = %d\n", file_attr.st_mtime);
#if 1
/* 超过若干天 */
if (TimeStamp::now().Seconds() - file_attr.st_mtime > log_life)
{
printf("remove file '%s'\n", file);
remove(file);
}
#endif
num++;
printf("while....\n");
}
printf("num = %d\n", num);
closedir(dir);
system("pause");
}
三、附带timestamp.h
#ifndef BASE_TIMESTAMP_H
#define BASE_TIMESTAMP_H
#include
#include
#include
#include
#include
#include
using namespace std;
#ifdef WIN32
#define snprintf _snprintf
#endif
class TimeStamp
{
public:
TimeStamp(int32_t seconds = 0, int32_t microseconds = 0)
: seconds_(seconds), microseconds_(microseconds)
{
}
int32_t Seconds()
{
return seconds_;
}
int32_t MilliSeconds()
{
return microseconds_ / kMicroSecondsPerMilliSecond;
}
int32_t MicroSeconds()
{
return microseconds_;
}
string ToString();
string ToFormattedString(bool show_microseconds = true);
static TimeStamp now();
static const int kMilliSecondsPerSecond = 1000;
static const int kMicroSecondsPerSecond = 1000000;
static const int kMicroSecondsPerMilliSecond = 1000;
private:
int32_t seconds_;
int32_t microseconds_;
};
#endif //BASE_TIMESTAMP_H
timestamp.cpp
#include "timestamp.h"
#include
#include
#ifdef WIN32
#include
#else
#include
#endif
#ifdef WIN32
int gettimeofday(struct timeval *tp, void *tzp)
{
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm.tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#endif
string TimeStamp::ToString()
{
char buf[32] = { 0 };
snprintf(buf, sizeof(buf), "%d.%06d", seconds_, microseconds_);
return buf;
}
string TimeStamp::ToFormattedString(bool show_microseconds)
{
char buf[64] = { 0 };
struct tm tm_time;
time_t t = static_cast(seconds_);
#if 0
/* gmtime转出来的是0时区的标准时间 */
gmtime_r(&t, &tm_time);
#else
/* localtime是将时区考虑在内了,转出当前时区的时间 */
localtime_s(&tm_time, &t);
#endif
if (show_microseconds)
{
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d.%06d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, microseconds_);
}
else
{
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
}
return buf;
}
TimeStamp TimeStamp::now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return TimeStamp(static_cast(tv.tv_sec), static_cast(tv.tv_usec));
}