第一部分 *百度百科提供的内容总结:WIN32_FIND_DAT
第二部分 *程序实例
第三部分 *一篇使用FindFirstFile和FindNextFile函数的博文
第一部分
1.关于文件的全部属性信息,有以下以下9种:
文件的标题名、文件的属性(只读、存档,隐藏等)、文件的创建时间、文件的最后访问时间、文件的最后修改时间、文件大小的高位双字、文件大小的低位双字、保留、保留。在这里只有文件标题名和文件的长度可以通过CFile类比较方便的获得,而对于其他几种属性的获取和设置就无能为力了。
在用findfirst()和findnext()函数去查找磁盘文件时经常使用的一个数据结构WIN32_FIND_DATA的成员变量里包含了以上所有的文件属性,因此可以通过这个结构作为获取和更改文件属性的手段。该结构的内容如下:
typedef struct _WIN32_FIND_DATA
{
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名(最多可达 255 个字符的长文件名),带句点和扩展名
TCHAR cAlternateFileName[ 14 ]; //8.3格式文件名(句点前最多有8个字符,而扩展名最多可以有3个字符)
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
可以通过FindFirstFile()函数根据当前的文件存放路径查找该文件来把待操作文件的相关属性读取到WIN32_FIND_DATA结构中去:
WIN32_FIND_DATA ffd ;
HANDLE hFind = FindFirstFile("c:\\test.dat",&ffd);
在使用这个结构时不能手工修改这个结构中的任何数据,结构对于开发人员来说只能作为一个只读数据,其所有的成员变量都会由系统完成填写。在MSDN帮助中可以查找到关于WIN32_FIND_DATA结构的更加详细的说明。
2.文件属性信息的获取与更改
为了更好的保存获取到的文件属性信息,对应于文件属性构造一个自定义的FILE_INFO数据结构,获取的属性信息可暂存于此:
typedef struct _FILE_INFO
{
TCHAR szFileTitle[128]; //文件的标题名
DWORD dwFileAttributes; //文件的属性
FILETIME ftCreationTime; //文件的创建时间
FILETIME ftLastAccessTime; //文件的最后访问时间
FILETIME ftLastWriteTime; //文件的最后修改时间
DWORD nFileSizeHigh; //文件大小的高位双字
DWORD nFileSizeLow; //文件大小的低位双字
DWORD dwReserved0; //保留,为0
DWORD dwReserved1; //保留,为0
} FILE_INFO, * PFILE_INFO;
首先用FindFirstFile()函数将文件属性获取到WIN32_FIND_DATA 结构对象FindFileData中去,之后可以用FindClose()将其关闭,并把FindFileData中的有关文件属性信息的内容复制到自定义结构FILE_INFO的结构对象FileInfo中备用。下面是关于这部分描述的部分关键代码:
//声明结构对象
FILE_INFO FileInfo;
WIN32_FIND_DATA FindFileData;
……
//获取文件属性信息
FindClose(FindFirstFile("Test.txt",&FindFileData));
memset(&FileInfo,0,sizeof(FILE_INFO));
……
//将文件属性信息保存到FileInfo中备用
strcpy(FileInfo.szFileTitle,myFile.GetFileTitle());
FileInfo.dwFileAttributes = FindFileData.dwFileAttributes;
FileInfo.ftCreationTime = FindFileData.ftCreationTime;
FileInfo.ftLastAccessTime = FindFileData.ftLastAccessTime;
FileInfo.ftLastWriteTime = FindFileData.ftLastWriteTime;
FileInfo.nFileSizeHigh = FindFileData.nFileSizeHigh;
FileInfo.nFileSizeLow = FindFileData.nFileSizeLow;
……
在获取到文件的原始属性信息后既可以原封不动的将属性重新写到文件,也可以对其中某一项或某几项属性内容进行修改后再行写入文件,从而达到更改文件属性的目的。比如可以用SetFileTime()函数设置文件的创建时间、最近一次访问时间以及最近一次修改的时间等等:
SetFileTime((HANDLE)destFile.m_hFile, //待写入的文件句柄
&FileInfo.ftCreationTime, //文件的创建时间
&FileInfo.ftLastAccessTime, //文件最近一次的访问时间
&FileInfo.ftLastWriteTime); //文件最近一次的修改时间
也可以用SetFileAttributes() 函数实现对文件属性的修改:
SetFileAttributes(FileInfo.szFileTitle,FileInfo.dwFileAttributes);
至于文件名的修改则更加简单,直接在创建文件时在CreateFile()或CFile类的成员函数Open里直接对文件名参数进行设置即可。
移动一个文件,一般可使用:
BOOL WINAPI MoveFile(LPCSTR Existing, LPCSTR Target);
其中 Existing是现有文件或目录,Target是目标文件或目录。此函数还可以用来对文件改名。例:
MoveFile("D:\\Temp\\a.txt", "E:\\MyPath\\b.txt"); 将D:\Temp\a.txt移动到E:\MyPath并改名为b.txt
3.>>API中的CopyFile:<<
BOOL WINAPI CopyFile(LPCSTR oldFileName, LPCSTR newFileName, BOOL failIfExists);
oldFileName: 原始文件名;
newFileName: 目标文件名;
failIfExists: 目标文件已存在时的处理办法。TRUE则不覆盖并返回失败标记,FALSE则覆盖。
返回值:TRUE代表成功,FALSE代表失败。
4.>>粘贴又是哪个函数呢?<<
粘贴的功能不是一个函数能够实现的.对剪贴板的操作Windows有一系列函数.粘贴必须要用的是 OpenClipboard(), GetClipboardData()和CloseClipboard().
拷贝-粘贴的对象为文本或其他二进数据(如图像)时可能还要辅助其他的API.
---------------------------------百度百科引用内容结束----------------
第二部分
5. _access(char *); //判断文件或文件夹路径是否合法 <direct.h>
_chdir(char *); //设置当前目录 <direct.h>
6.补充findfirst, findnext 和_findfirst, _findnext搜索磁盘目录
四个函数全部属于 io.h 头文件中
1. int findfirst(char *pathname, struct ffblk *ffblk, int attrib);
2. int findnext(struct ffblk *ffblk);
程序例1:
/*用于文件的查找和删除*/
#include <stdio.h>
#include <dir.h>
//两个函数需要定义一个结构体来存储函数返回的数据。结构体如下:
struct ffblk
{
char ff_reserved[21]; /*DOS保留字*/
char ff_attrib; /*文件属性*/
int ff_ftime; /*文件时间*/
int ff_fdate; /*文件日期*/
long ff_fsize; /*文件长度*/
char ff_name[13]; /*文件名*/
}
//将结构体中的ff_name[13]显示出来即可。
int main(void)
{
struct ffblk ffblk;
int done;
printf("Directory listing of *.*\n");
done = findfirst("*.*",&ffblk,0);
while (!done)
{
printf(" %s\n", ffblk.ff_name);
done = findnext(&ffblk);
}
return 0;
}
3. int _findnext(intptr_t handle,struct _finddata_t *fileinfo); \\搜索与_findfirst函数提供的文件名称匹配的下一个实例,若成功则返回0,否则返回-1
4. long _findfirst( char *filespec, struct _finddata_t *fileinfo ); \\搜索与指定的文件名称匹配的第一个实例,若成功则返回第一个实例的句柄,否则返回-1L
参数说明:struct _finddata_t的定义见于io.h
struct _finddata_t
{
unsigned attrib;
time_t time_create; /* -1 for FAT file systems */
time_t time_access; /* -1 for FAT file systems */
time_t time_write;
_fsize_t size;
char name[260];
};
shmiloveyou 补充:sttrib参数有如下类型:
_A_ARCH(存档)
_A_HIDDEN(隐藏)
_A_NORMAL(正常)
_A_RDONLY(只读)
_A_SUBDIR(文件夹)
_A_SYSTEM(系统)
程序例2--遍历指定目录下的文件和文件夹:
shmiloveyou:特别注意,这个实例有个严重的问题,遍历会失败
///原作者已经不详,我会在“程序例3中用实例指出问题所在”
显示所有的文件:
#include <io.h>
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
string sRoot = "D:";
string sSuffix = "\\test\\*.*"; // 后缀 //shmiloveyou:这样写只会查找文件,"\\*"表示匹配所有包括文件夹
void Move(string sPath);
void main()
{
Move(sRoot);
system("pause");
}
void Move(string sPath)
{
struct _finddata_t file;
long hFile, hFileNext;
string sPathLast = sPath + sSuffix; // sPathLast = "D:\\test\\*.*"
hFile = _findfirst(sPathLast.c_str(), &file);
if(hFile == -1)
{
cout<<"文件不存在."<<endl;
return;
}
else
{
cout<<file . name<<endl;
}
hFileNext = _findnext(hFile, &file); //shmiloveyou:这句多余
while(_findnext(hFile, &file) == 0)
{ //shmiloveyou:没有过滤掉本级目录'.'和父级目录'..'
if(file.attrib == _A_SUBDIR)
{
string sAddPath = sPath;
sAddPath += "\\";
sAddPath += file . name;
cout<<"目录:"<<sAddPath<<endl;
Move(sAddPath);
}
else
{
string sAddPath = sPath;
sAddPath += "\\";
sAddPath += file . name;
cout<<"文件:"<<sAddPath<<endl;
}
}
//shmiloveyou:作者没有关闭文件句柄 _closefile(hFile);
}
运行结果类似这样,总之是失败的:
总结:
本级目录和父目录”这个概念,因为_findfirst函数最先查找的就是本级目(此时file.name[0] == '.'必成立),而且还会查找父级目录(此时file.name[0]和file.name[1]都等于'.')。这就是为什么上面的图片中会有那么多的‘.’和‘..’。怎么办呢?添加一段程序处理这个问题,只要“ file.name[0] == '.' ”成立就继续while循环。程序如下:
程序例3:(shmiloveyou -- 原创 2014-01-04)
#include <io.h>
#include <iostream>
#include <string>
#include <windows.h>
#include <direct.h>
using namespace std;
string sRoot = "G:"; //根目录
string sSuffix = "\\DesignModal"; //中间路径
string sEnd = "\\"; //路径末尾追加的部分
void Move(string sPath);
int main(void)
{
string sPath = sRoot + sSuffix; //sPath="G:\\DesignModal"
Move(sPath);
system("pasue");
return 0;
}
void Move( string sPath)
{
struct _finddata_t file;
memset(&file, 0, sizeof(file)); //初始化结构
long hFile;
string sPathLast = sPath; //sPathLast="G:\\DesignModal"
if (_chdir(sPathLast.c_str()) != 0) //_chdir函数设置当前目录
{
cerr << "当前目录设置失败!" << endl;
exit(-1);
}
hFile = _findfirst("*",&file); //参数1:char *类型,"*"表示通配符,可以查找文件、文件夹
if(hFile == -1)
{
cout << " 遍历失败!" << endl;
return ;
}
do
{
if(file.name[0] == '.') //过滤本级目录和父目录 此处就是“程序例2”中的病根所在
continue;
if(file.attrib == _A_SUBDIR) //子文件夹
{
string sAddPath = sPath;
sAddPath += "\\";
sAddPath += file.name;
cout << "目录:" << sAddPath << endl;
Move(sAddPath); //递归遍历
}
else
{
string sAddPath = sPath;
sAddPath += "\\";
sAddPath += file.name;
cout << "文件:" << sAddPath << endl;
}
}while(_findnext(hFile,&file) == 0); //没有找到文件或文件夹
_findclose(hFile); //一定记得关闭文件句柄
}
运行效果:
第三部分
上面的博文中作者使用FindFirstFile和FindNextFile这两个函数实现指定目录遍历的。
避免参考博文丢失(最初博文地址已经失效),遂引用如下:
//转自:http://www.vcgood.com/forum_posts.asp?TID=2261&PN=1 地址已经失效
//用于输出指定目录下的所有文件的文件名,包括子目录。
版本1:用string处理,方便,容易理解.
#include <windows.h>
#include <iostream>
#include <string>
using namespace std;
bool IsRoot(string Path)
{
string Root;
Root=Path.at(0)+"://";
if(Root==Path)
return true;
else
return false;
}
void FindInAll(string Path)
{
string szFind;
szFind=Path;
if(!IsRoot(szFind))
szFind+="//";
szFind+="*.*";
WIN32_FIND_DATA FindFileData;
HANDLE hFind=FindFirstFile(szFind.c_str(),& FindFileData);
if(hFind==INVALID_HANDLE_VALUE)
return ;
do
{
if(FindFileData.cFileName[0]=='.') //过滤本级目录和父目录
continue;
if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //如果找到的是目录,则进入此目录进行递归
{
string szFile;
if(IsRoot(Path))
szFile=Path+FindFileData.cFileName;
else
szFile=Path+"//"+FindFileData.cFileName;
FindInAll(szFile);
}
else //找到的是文件
{
string szFile;
if(IsRoot(Path))
szFile=Path+FindFileData.cFileName;
else
szFile=Path+"//"+FindFileData.cFileName;
cout<<szFile<<endl;
cout<<FindFileData.cFileName<<endl;
}
}
while(FindNextFile(hFind,& FindFileData));
FindClose(hFind);
}
int main()
{
FindInAll("D://C++");
return 0;
}
版本2:编译器的通用性更强
#include <windows.h>
#include <iostream>
using namespace std;
BOOL IsRoot(LPCTSTR lpszPath)
{
TCHAR szRoot[4];
wsprintf(szRoot,"%c://",lpszPath[0]);
return (lstrcmp(szRoot,lpszPath)==0);
}
void FindInAll(::LPCTSTR lpszPath)
{
TCHAR szFind[MAX_PATH];
lstrcpy(szFind,lpszPath); //windows API 用lstrcpy,不是strcpy
if(!IsRoot(szFind))
lstrcat(szFind,"//");
lstrcat(szFind,"*.*"); //找所有文件
WIN32_FIND_DATA wfd;
HANDLE hFind=FindFirstFile(szFind,& wfd);
if(hFind==INVALID_HANDLE_VALUE) // 如果没有找到或查找失败
return;
do
{
if(wfd.cFileName[0]=='.')
continue; //过滤这两个目录
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
TCHAR szFile[MAX_PATH];
if(IsRoot(lpszPath))
wsprintf(szFile,"%s%s",lpszPath,wfd.cFileName);
else
wsprintf(szFile,"%s//%s",lpszPath,wfd.cFileName);
FindInAll(szFile); //如果找到的是目录,则进入此目录进行递归
}
else
{
TCHAR szFile[MAX_PATH];
if(IsRoot(lpszPath))
wsprintf(szFile,"%s%s",lpszPath,wfd.cFileName);
else
wsprintf(szFile,"%s//%s",lpszPath,wfd.cFileName);
printf("%s/n",szFile); //对文件进行操作
}
}
while(FindNextFile(hFind,&wfd));
FindClose(hFind); //关闭查找句柄
}
int main()
{
FindInAll("D://C++");
return 0;
}