最近在看MFC 的文件操作,从网上淘了一本比较讲MFC文件操作比较全的电子书,然后开始试手。在删除文件夹时,遇到了问题,通过查看MSDN和网友的论坛留言,自己总算是解决了这个问题,现在与大家共享。
MFC中提供了删除文件夹的一个封装函数 RemoveDirectory(LPCTSTR lpPathName),我们只要把要删除的文件夹的路径传进去就可以删除了,貌似一切如此简单。我象征性的建立一个文件夹,然后在程序中删除了它,呵呵,一下就成功了。正当我要转手去做另外的操作时,我喜欢乱尝试的毛病就鬼使神差的让我做了这么一件事,在这个文件夹下我添加了几个新的子文件夹以及一些文件,这下我再试我的程序时就出现问题了,删不掉了!!
原来-------RemoveDirectory(LPCTSTR lpPathName)欺骗了我,他只能删除空的文件夹,这下我有问题了,怎么才能删除一个文件夹,即便其中含有无数的子文件和子文件夹呢?
RemoveDirectory(LPCTSTR lpPathName) 的这种行为其实是情有可原的,它为我们的操作提供了一种安全级别的控制。
但我现在就想删除非空文件夹 , 怎么办呢????
递归删除!!
看到论坛上有人提出这一思想,我感觉很有道理,于是就基于这一思想,着手实现它!
首先将文件夹下所有内容删除,再调用RemoveDirectory(LPCTSTR lpPathName)不就可以实现了么!!
为此我专门写了一个函数来递归删除一个文件夹下的所有内容!
请看我的代码
- void myDeleteDirectory(CString directory_path) //删除一个文件夹下的所有内容
- {
- CFileFind finder;
- CString path;
- path.Format("%s/*.*",directory_path);
- BOOL bWorking = finder.FindFile(path);
- while(bWorking){
- bWorking = finder.FindNextFile();
- if(finder.IsDirectory() && !finder.IsDots()){//处理文件夹
- myDeleteDirectory(finder.GetFilePath()); //递归删除文件夹
- RemoveDirectory(finder.GetFilePath());
- }
- else{//处理文件
- DeleteFile(finder.GetFilePath());
- }
- }
- }
定义一个 CFileFind 类对象 来找文件夹下的所有子文件和子文件夹,然后依次判断它是 文件 还是 文件夹,
如果是文件 就直接删除了,如果是文件夹就递归调用 该 myDeleteDirectory()函数,来删除其内容。然后在调用RemoveDirectory()来删除这个文件夹,这样不就好了么?
为了测试我的程序是否是正确的,我创建了一个文件夹 ForVcTest,在其中添加了许多的子文件和文件夹,又在子文件夹中添加了文件和文件夹。写了这么一段测试程序,诸位请看:
- void DeleteAllDirectory() //删除文件夹 包括非空的文件夹
- {
- //第一步 删除 该文件夹下所有文件
- myDeleteDirectory("C:/ForVcTest");
- //第二步 删除该空文件夹
- RemoveDirectory("C:/ForVcTest");
- }
测试结果出来了,我眼看着一个内容丰富的文件夹,就在我的程序运行后消失了!真是很舒心啊!呵呵!
上一篇博文中已经提到了CFileFind类,并且用它实现了删除任意文件夹,此处不再对其赘述。
在实现文件复制过程时,一个问题i是我纠结了许久,CFileFind 类的成员函数 GetFilePath()与GetFileName(),其实很好理解的两个函数,一个是获得文件的路径,一个是获得文件的名字。但我却在理解上犯了一个错误,就是文件路径究竟包不包含文件名字,如有文件C:/Test/1.txt ,那么它的路径和名称分别是什么?我理解成了,路径:C:/Test 名称1.txt ,我按照我的理解编写这个复制功能的实现代码,老师出错,最后我才发现,原来 路径是包含文件名称的 ,即上面的文件 路径即是:C:/Test/1.txt。
似乎是个很低级的错误,但是的确困扰了我一段时间。
回到正题。
具体的实现思想类似于上一篇博文“MFC 中 删除一个非空文件夹”
请看源代码:
- void myCopyDirectory(CString source, CString target)
- {
- CreateDirectory(target,NULL); //创建目标文件夹
- //AfxMessageBox("创建文件夹"+target);
- CFileFind finder;
- CString path;
- path.Format("%s/*.*",source);
- AfxMessageBox(path);
- bool bWorking = finder.FindFile(path);
- while(bWorking){
- bWorking = finder.FindNextFile();
- AfxMessageBox(finder.GetFileName());
- if(finder.IsDirectory() && !finder.IsDots()){ //是文件夹 而且 名称不含 . 或 ..
- myCopyDirectory(finder.GetFilePath(),target+"/"+finder.GetFileName()); //递归创建文件夹+"/"+finder.GetFileName()
- }
- else{ //是文件 则直接复制
- //AfxMessageBox("复制文件"+finder.GetFilePath());//+finder.GetFileName()
- CopyFile(finder.GetFilePath(),target+"/"+finder.GetFileName(),FALSE);
- }
- }
- }
源代码不是很难理解,不再详解
一、 从路径中 提取扩展名
- CString path("C:/ForVcTest/diary.txt");
- CString ext = path.Mid(path.ReverseFind('.')+1);
- AfxMessageBox(ext);
解析:1. CString::Mid
CString Mid(int nFirst) const;
CString Mid(int nFirst,int nCount) const;
nCount代表要提取的字符数,nFirst代表要提取的开始位置
2. CString::CString::ReverseFind
int ReverseFind( TCHAR ch ) const;
返回值
返回此CString 对象中与要求的字符匹配的最后一个字符的索引;如果没有找 到需要的字符则返回-1。
参数
ch 要搜索的字符
3. 从文件路径中找到 ' . '的位置,然后索引后移一个即是后缀名的起始字符的索引
利用Mid函数提取出 后缀名
二、从路径中 提取文件名
- CString path("C:/ForVcTest/diary.txt");
- CString name = path.Mid(path.ReverseFind('/')+1);
- AfxMessageBox(name);
三、获取文件属性
- DWORD dwAttr = GetFileAttributes("C:/ForVcTest/2.txt");//获取文件的属性
- if (dwAttr == FILE_ATTRIBUTE_ARCHIVE){
- AfxMessageBox("FILE_ATTRIBUTE_ARCHIVE");
- }
四、设置文件属性
- SetFileAttributes("C:/ForVcTest/2.txt",FILE_ATTRIBUTE_READONLY);//|FILE_ATTRIBUTE_HIDDEN
五、获取当前程序所在路径
- //提取文件路径
- char appName[_MAX_PATH];
- GetModuleFileName(NULL,appName,_MAX_PATH);
- CString szPath(appName);
- AfxMessageBox(szPath);
解析:DWORD WINAPI GetModuleFileName(
__in_opt HMODULE hModule,
__out LPTSTR lpFilename,
__in DWORD nSize);
返回包含指定模块的文件的全路径,这个模块必须是已经被当前进程加载的。
六、移动文件
- MoveFile("C:/ForVcTest/diary.txt","C:/ForVcTest/newCopy.txt");
移动后 源文件被删除,目标文件被创建
七、Path Name Title 的区别
- CFile file("C:/ForVcTest/newCopy.txt",CFile::modeRead);
- CString szPath = file.GetFilePath();
- CString szName = file.GetFileName();
- CString szTitle = file.GetFileTitle();
- AfxMessageBox("szPath = "+szPath);
- AfxMessageBox("szName = "+szName);
- AfxMessageBox("szTitle = "+szTitle);
我写了上面一段测试程序,得到的结果是
szPath = "C:/ForVcTest/newCopy.txt"
szName = "newCopy.txt"
szTitle = "newCopy.txt"
MSDN 里面说 title 是 newCopy 但是我的运行结果和它讲的不一样。
这里我就不是很明白了,这后两个概念到底有什么区别?
我又研究了一番,终于发现了他们的区别。
如果将文件的后缀名 隐藏以来,你就发现,name = newCopy.txt 而 title = newCopy
这就是区别吧。
希望看这篇文章的博友能和我一起交流讨论这个问题。
八、文件分隔
- bool SplitFile()
- {
- //文件分割
- CFile m_File;
- CString m_FileName,m_FileTitle,m_FilePath;
- m_FilePath = "C://ForVcTest//newCopy.txt";
- char pBuf[40];
- if(m_File.Open(m_FilePath,CFile::modeRead | CFile::shareDenyWrite))
- {
- m_FileName=m_File.GetFileName();
- m_FileTitle=m_File.GetFileTitle();
- // DWORD FileLength=m_File.GetLength();
- // DWORD PartLength=FileLength/2+FileLength%2;
- int nCount=1;
- CString strName;
- CFile wrFile;
- DWORD ReadBytes;
- while(true)
- {
- ReadBytes=m_File.Read(pBuf,40); //ReadBytes 实际读取的字节数
- strName.Format("C://ForVcTest//%s%d.txt",m_FileTitle,nCount);
- wrFile.Open(strName,CFile::modeWrite | CFile::modeCreate);
- wrFile.Write(pBuf,ReadBytes);
- wrFile.Close();
- if(ReadBytes<40) //实际读取的字节数 不足 分配的大小,则说明文件读完了
- break;
- nCount++;
- }
- m_File.Close();
- }
- else{
- AfxMessageBox("不能打开文件");
- return fasle;
- }
- return true;
- }