目录处理函数 # j" L) }" w: i" x
编程语言:C++ Builder 作者:王行舟 3 T2 r& k8 N0 R/ x/ K D
在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。
0 K$ Q, P1 J) H' P1 J一、判断目录是否存在: 7 T$ h" Y' |. ]9 H; Q: G
C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:
: L7 q6 [6 m- Z/ j& A设char *Dir为带判断的目录
+ Q+ q2 V- B, Y% y5 K" Ebool Exist; // 最后结果,表示目录是否存在
1 i; O( O7 C7 t! }if(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\”
' v5 j+ I4 R. `* ~! uWIN32_FIND_DATA wfd; // 查找
, A! v$ R6 _ k" B% LHANDLE hFind=FindFirstFile(Dir,&wfd); % q# \5 Y6 ~5 J& X
if(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在; N7 D$ k" K2 E, X+ [( e
else* k1 }; ?/ k$ ~/ X+ d
{: f/ R- ^$ U! [, I% _; Z) h
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录) J6 X4 j/ g% U; A* ?' c
Exist=true; // 是目录,目录存在
# `2 r5 [1 h; I0 p0 d- q else
3 P; p, O+ j8 X8 n& m/ K; v Exist=false; // 是目录,目录不存在4 }+ ~. D. h5 J% R9 K
FindClose(hFind);
, f& L- e, H. P* t$ G) ^- v} ! Z4 {6 s/ f# x
二、打开目录选择对话框选择一个目录:
$ d# E$ P$ f+ `0 A6 \5 e( ^9 y 大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示: . f, i; l6 H7 k0 V$ t
HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle % X1 y9 D7 r" y
LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL
! h8 b1 k$ I6 TLPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置
2 S. p7 ?) X( y- s5 oLPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置
) D. _, R& b+ e. lUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS
e5 V$ i9 S0 k. ^+ yBFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL
. l0 J; S8 a4 {- VLPARAM lParam; // 预定义的对话框传递给回调函数的值
' S1 t2 v+ N) V) w2 O3 V/ s1 Dint iImage; // 与所选目录相关联的图标在系统图标集合中的索引
6 C! t2 A# K! }4 \可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下:
9 V6 b( H6 f( |; h! r#include <shlobj.h> // 必须包含的头文件4 |) B- |9 O# q. V5 w
char SelectedDir[MAX_PATH]; // 最终结果
- {. ?* L Z! P1 n5 k' l! dBROWSEINFO bi; // 入参
" v0 u$ F9 {* Echar FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font
6 _9 [! N! E3 c* L+ ~7 U- K$ HLPITEMIDLIST ItemID; // 所选目录的系统标志指针
' ~* e! g# _4 y6 ]8 m; _memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果
* N% ?, p+ J$ W7 e5 k8 u; _memset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据$ Q! a" G0 J2 |
bi.hwndOwner = Application->Handle;! k& p# i! H* n
bi.pszDisplayName = FolderName;0 B' P D+ N) T7 B
bi.lpszTitle = "请选择目录"; // 改成自己希望的
6 }/ F5 o8 P3 H- i) e5 p) lbi.ulFlags=BIF_RETURNONLYFSDIRS;
% A1 c" I6 [" d3 I( OItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框
; j$ V4 @0 p! R# t# k8 F5 qif(ItemID)
* } a. _' K+ r# d9 [{
7 A. w; } n, P SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名3 j/ d& @0 X5 @5 i; s" ?
GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放
# j4 I, Z( d: {}
# l: y& J0 G2 D) y* N三、直接建立多级目录:
4 W3 ^( b9 [4 g3 v* T2 r) m2 S8 \ Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。 # K' X" y- ?6 B2 D% u
bool MakeDirectoryEx(const AnsiString & ) // 入参为打算创建的目录名,根据操作结果返回"true"或"false"
2 e. q4 R. [8 Y+ j{
$ E# T G# \: f! e! _ if(P.IsEmpty())return false;
; T" D2 y1 E3 [& L int len=P.Length();
% |4 g+ |0 N2 j; k& `1 b char *Path=P.c_str();; y7 N- X' t3 l; e3 N3 m
if(Path[len-1]=='\\')# ~4 w. a6 p7 ^0 g/ f5 l2 }! Q
{
( t/ M: q3 x$ t1 W/ i0 P7 m len--;6 D7 M7 S2 W* ?. w; i: T6 h
Path[len]='\0';
5 V2 W7 l* j9 q! \% d } // 删除末尾的"\"$ S5 Y: `: G" G3 B; d4 {
AnsiString Dir=Path;
$ @! l P j2 E* V // 分开父目录和本身目录名称1 s! R A$ g8 W1 f1 X
AnsiString Parent;
- e h9 U3 b9 S! S8 e0 C! ? for(int i=len-1;i>0;i--)
4 d- F8 g- ]& Y7 h5 F- |6 W/ V, \ {7 r' E |2 n3 b* C2 T% p% d: O
if(Dir.IsPathDelimiter(i))
' x- [* [' \1 E) D5 P {2 W7 y+ d: M- I( b
Parent=Dir.SubString(0,i); {/ H- x) x& }/ L: l" B6 F
break;" ~ o+ ]' V. Y- n9 G
}
. j: P4 M! G/ K W8 f! {! f }5 k" {: p: G* B0 @0 }
if(Parent.IsEmpty())return false; // 目录名称错误. F. a( v" h d- z
bool Ret=true;
9 ]& `6 W$ _2 G, z. a/ X. D if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录
' H9 T) `5 X& e3 u' n3 H Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在
- m# a1 o. _2 I! L0 n7 {7 _4 r( ` if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录4 P/ Q& u9 k8 ~3 F
if(Ret) // 父目录存在,直接创建目录0 r5 A T$ R" O% w& q0 T
{9 D$ p8 w1 c) D. C- m+ y
SECURITY_ATTRIBUTES sa;
) ~; P M$ | O s8 @* O9 s; A sa.nLength=sizeof(SECURITY_ATTRIBUTES);
$ ]. j% u; Z9 \2 |/ e! I sa.lpSecurityDescriptor=NULL;
5 k8 ~$ O4 c8 t( @* l sa.bInheritHandle=0;8 {2 t8 t& E' Z
Ret=CreateDirectory(Path,&sa);
- k& d/ _3 A) m6 Z" y; P+ a, c: u( \ }
3 X) d- Y9 B' L9 A- s' \! P return Ret;
: s; O9 I$ r. P. W4 j2 t T0 M/ J} 3 O" j/ Q$ Q% p, `7 j. X- d
可以看出基本方法是:
5 ]: Z% T% I [; q6 O. ]. W先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; . d1 T/ x% c5 }( ?
如果父目录存在,则直接创建目录,否则自我调用创建父目录。 F* V/ X# q+ x* R
% L. L6 G# L- ]( w8 ~% R四、直接删除整个目录:
. n- ], @# ~( X% y& s- B& D' d, B 在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:
* X) v I( A% X; _3 X( w查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*)
- l- A& L! }# {" T! A如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 5 ]. C/ m; A* n" |! f1 u
如果找到目录,则进行自我调用,即开始递归过程。
( h0 ~, M7 a7 Z: q# w如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。
2 a+ m t8 J3 \% j$ _具体程序代码如下:
% o* U9 v3 X: `bool DeleteDirectoryEx(const AnsiString & )
+ _: k( ^4 D! C{
K+ N0 }6 C5 ~/ [ [% p9 ~ if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白6 p6 C' I1 b& O( |7 r5 ]+ L
int len=P.Length();
6 L1 i% w( U5 {( U, D) Q char *Path=P.c_str();( _3 @: b5 N* s( N" ^; z: k
AnsiString Dir=Path;& O7 q- ^4 z, U0 ]! B) h
if(Path[len-1]!='\\')Dir=Dir+'\\';
: |; i% K, X4 y+ [; _* @: { AnsiString Files=Dir+"*.*";1 t R5 @' b. |/ p
WIN32_FIND_DATA wfd;, Y0 q! p; n% f; S0 Z5 x( x% \
HANDLE hFind=FindFirstFile(Files.c_str(),&wfd);4 Z# J; L: b, v) X' a' V) |( ?
bool Ret=true;: w3 a5 B0 @ n/ G C( N
AnsiString Tmp;
% a3 o. j' I6 ?9 ` if(hFind!=INVALID_HANDLE_VALUE)+ r9 R' c2 M+ D& K: z' r4 @
{$ [* j$ e9 f; d9 V' D/ K3 e% p
bool bFind=true;- o2 f, @* [' O6 J( I1 C# h
while(bFind)1 l% M5 I2 _5 R* e3 N$ [2 k' i
{: ? { y* a" L% ?, K
if(wfd.cFileName[0]!='.') // . ..
1 S" R- p) E8 U% S: | {
A3 L9 ?0 @& |4 D9 M. T( f! D Tmp=Dir+wfd.cFileName;
* E( `, J) e. E( C# u. P if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)# }- ]% p+ e/ l
{ // 删除所有子目录
& @. Y1 ]$ w2 J2 S" ]; G# _ Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);
; m9 h5 q9 l" F% ^- t& P }else
% G+ z' W# t5 e3 e5 f7 n { // 删除所有文件
2 j6 \9 O: A5 T4 R& [ SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL);% \/ n! Q1 m) w W1 Z: n4 X3 b
Ret=Ret&&DeleteFile(Tmp.c_str());
( ?& c: F# Q5 ]% Z( R6 T }
' q. I8 N8 d& b4 K/ h- _9 K }3 u1 w f' r# w) |
bFind=FindNextFile(hFind,&wfd);1 N+ c$ ^+ B. U* l! }$ J9 Y" Y. w
}
& o- n4 u' x+ Z, ~ FindClose(hFind);0 T. f: y; m8 P; Z+ S$ o3 `8 s$ E
}, v( a- j4 Z8 m4 y/ A* i9 L' B
if(Ret)return RemoveDirectory(Path);4 T$ }* A' b6 c5 k6 s4 r/ `# Y$ `
return false;! h6 N+ G8 b7 r5 h' [
} 9 q, J1 Y3 [4 }5 Y
完
# b" m$ W. r3 c- H8 T* C
) a, \# ~3 t; o& v" z" |- { |