数学建模社区-数学中国

标题: 目录处理函数 [打印本页]

作者: 韩冰    时间: 2005-1-26 13:18
标题: 目录处理函数

目录处理函数

4 I0 X& t- @( g% ~3 @0 e

编程语言:C++ Builder 作者:王行舟

v* z: ]1 N( K O6 Z0 N2 ^& }

在编程时,经常有一些针对目录的操作,如打开目录对话框选择一个目录,直接创建多级目录,直接删除多级目录,判断某个目录是否存在等。本文就这些问题给出编程实现方法,并给出详细的程序代码,供各位编程爱好者参考。

( p$ J* I& a9 V7 j0 S( D2 J5 t

一、判断目录是否存在:

% C/ O- O5 C+ g2 H

C++ Builder中提供了检查文件是否存在的函数FileExists,但没有提供检查目录是否存在的函数,我们可以用Windows API函数FindFirstFile实现这个功能。程序实现如下:

9 p, b& L. f, Q+ a' L1 H4 F

设char *Dir为带判断的目录 0 U# l0 r& m, D3 O7 |2 j [( h: sbool Exist; // 最后结果,表示目录是否存在 # r5 @# d0 ^, r# c/ C5 wif(Dir[strlen(Dir)]=='\\')Dir[strlen(Dir)-1]='\0'; // 先删除最后的“\” $ C! a7 G7 r5 ]( C- o- k2 oWIN32_FIND_DATA wfd; // 查找, l. W5 w; P) P+ d; H& ~ HANDLE hFind=FindFirstFile(Dir,&wfd); . {% `+ \: X x" O* Z0 r$ U4 Pif(hFind==INVALID_HANDLE_VALUE)Exist=false; // 没有找到配备,目录肯定不存在 0 D7 }$ ?- m* X, a7 r3 D: melse% Q8 O! V6 r+ {- Q& H { / S1 X) q. H8 P( D% {6 @3 x if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // 检查找到的结果是否目录; K& t8 A2 I, j# I+ ` Exist=true; // 是目录,目录存在 2 | o/ U# D7 T3 y/ n2 M' O; L else 9 |2 H% e# N$ s0 c/ y3 x. g Exist=false; // 是目录,目录不存在 |* d) g0 L3 B/ n* K7 V3 [ FindClose(hFind); ( I- H- ]$ c S}

& O* ~* E5 k5 Z& v' _

二、打开目录选择对话框选择一个目录:

' H; }5 p# c; T; K$ u3 B

大多专业软件在要求输入目录的编辑框旁都放了一个按钮,点击后打开一个目录窗口,很多编程爱好者也希望能掌握这个方法。实现这个功能要调用Windows API函数SHBrowseForFolder,完整声明为WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder(LPBROWSEINFO lpbi),返回一个ITEMIDLIST类型的指针,通过这个指针调用函数SHGetPathFromIDList可以确定所选择的目录的全名称。入参为BROWSEINFO结构的指针,这个结构较为复杂,成员如下所示:

8 w! L$ b( K% s3 K9 ]8 u

HWND hwndOwner; // 拥有对话框的窗口,可以设置为Application->Handle R# d" }7 y e% z) _1 g* v LPCITEMIDLIST pidlRoot; // ITEMIDLIST类型的指针,表示在哪个路径下选择,一般可以设置为NULL/ P0 z% ~! W" @* u LPSTR pszDisplayName; // 选择后,所选目录的名称(不包含父级目录)被拷贝到这个指针指向的位置 ( {6 @# p' @" u7 I LPCSTR lpszTitle; // 作为标题显示在对话框中目录树的上面,可以根据实际情况设置 * ^# s V/ |* q/ G& S4 D* pUINT ulFlags; // 标志位,有点复杂,一般设置为BIF_RETURNONLYFSDIRS 3 d0 H5 Q' U z4 \3 ? S: m1 t3 E BFFCALLBACK lpfn; // 回调函数,一般不用,设置为NULL 8 c) n. P; n* w. p! S4 WLPARAM lParam; // 预定义的对话框传递给回调函数的值 ! C+ r5 \+ g+ f @int iImage; // 与所选目录相关联的图标在系统图标集合中的索引 6 m% p: \3 n6 y K 可以看出,使用函数SHBrowseForFolder还真麻烦,普通爱好者掌握它确实有一定的难度,现给出完整程序段如下: + Z/ o/ B; y% d5 X" _ #include <shlobj.h> // 必须包含的头文件 ( u% @+ F1 G4 H0 ~2 m% b( f8 Y* Vchar SelectedDir[MAX_PATH]; // 最终结果 $ [, G, F G; r: R( u$ @8 eBROWSEINFO bi; // 入参1 ^$ f+ A2 C' ]) t- I char FolderName[MAX_PATH]; // 所选目录名称,例如选择C:\Windows\Font,则为Font 1 Q; j3 C/ P! T d/ ]% i% f% QLPITEMIDLIST ItemID; // 所选目录的系统标志指针

, ?- G6 c1 U$ [: `

memset(SelectedDir, 0, MAX_PATH); // 初始化最终结果 9 T# O N1 G9 ^+ Z; r% K: t( |: bmemset(&bi, 0, sizeof(BROWSEINFO)); // 初始化入参所有数据: Z) }, b$ o0 d2 k/ ~ bi.hwndOwner = Application->Handle;) q6 ]+ G, R; Z9 S+ f2 P bi.pszDisplayName = FolderName;- t) `9 r* r1 H bi.lpszTitle = "请选择目录"; // 改成自己希望的$ X+ G* @8 x. b/ l bi.ulFlags=BIF_RETURNONLYFSDIRS;, @2 V5 @1 c: @- L5 h1 | ItemID = SHBrowseForFolder(&bi); // 调用函数,打开目录选择对话框 `- H" Q/ P6 f2 sif(ItemID) - i" V! X6 ^# Q# X{ }2 C- Z( i8 x8 ?3 w SHGetPathFromIDList(ItemID, SelectedDir); // 获取所选目录的全名7 V% [/ H. X' u3 V- j7 ] GlobalFree(ItemID); // 返回的ItemID占用了系统资源,不要忘了释放7 P6 I( j8 K* |( t% p }

* Q% [: S% q- o$ |

三、直接建立多级目录:

7 w. q# m+ B J+ }: q

Windows API提供了建立目录的函数CreateDirectory,但是调用前要保证父目录必须存在,否则会失败。其实,有时越级建立多级目录很有用,因为在建立目录特别是建立多层目录时,层层加以判断会大大地增加程序的复杂程度。如何实现这个功能呢?本人用递归方法设计了一个可以直接建立多级目录的函数,现说明如下,供各位朋友参考。

, L: e* z& K* c% A6 d3 ^' a# |9 d

bool MakeDirectoryEx(const AnsiString &) // 入参为打算创建的目录名,根据操作结果返回"true"或"false" 1 G: b6 Z5 |& I$ [8 n{ . j: t5 [# K, v if(P.IsEmpty())return false; 4 T; e/ F6 e. l% q( E int len=P.Length();+ V9 e8 b/ E: h. p- Z, A# m char *Path=P.c_str();' X' F8 X+ d; Y( y if(Path[len-1]=='\\') ; k8 V. Q' x7 ~4 D' x { 1 z9 P' r7 F5 ?/ L- Q len--; 6 m$ Q% R2 n/ m, L! O Path[len]='\0'; 9 b" ?$ Z' z. |9 E3 |0 P+ N } // 删除末尾的"\" $ V }. k7 m; V, X; Q AnsiString Dir=Path;% I: G3 e! _ W // 分开父目录和本身目录名称" X# S' e$ M- P' `) I AnsiString Parent; + D" {* |7 n/ z/ S) Q/ l for(int i=len-1;i>0;i--) ( U" n' w9 z! V) w { $ m+ `: \. j5 S- s- r( t& F5 @ S if(Dir.IsPathDelimiter(i)) / I! g3 _8 m6 E7 J; U/ j) y ? { 6 w, Q/ x3 `9 U& j Parent=Dir.SubString(0,i);# t1 N* Y2 O- N9 V9 ]6 g break;" R9 l, c& f, \1 R+ V2 P$ G5 { }6 A! n2 {$ R3 P# M- p }& b4 D. |# \8 k, }5 N% A if(Parent.IsEmpty())return false; // 目录名称错误 2 {8 q! }2 p2 \4 H1 T2 M! K/ f bool Ret=true; 7 ]- t3 i9 ~2 c+ B" Y* J0 k0 a if(Parent.Length()>3) // 如果长度小于3,表示为磁盘根目录, ~, b J3 U& O9 Q" r Ret=DirectoryExistEx(Parent.c_str());// 检查父目录是否存在( M3 @$ ` G. o R1 f! @3 x+ a' j if(!Ret)Ret=MakeDirectoryEx(Parent); // 父目录不存在,递归调用创建父目录; x+ j1 G! H* ~5 j( v9 S3 U if(Ret) // 父目录存在,直接创建目录 8 h( k( s" f& N7 G. G$ y# d' h { ; h+ J( T! j6 j) r; H+ K SECURITY_ATTRIBUTES sa;$ T7 ]$ B6 u1 p) x9 Z9 L sa.nLength=sizeof(SECURITY_ATTRIBUTES);7 o; Q w- J* u( T: ~1 M- H0 c' a' X; e sa.lpSecurityDescriptor=NULL;/ U8 {" v8 `) t! t3 b' m5 Y* l sa.bInheritHandle=0; 8 | j$ E, v/ x* ? Ret=CreateDirectory(Path,&sa);$ f& N9 R+ X7 |+ w$ t! @ }4 t5 k) H6 C7 @4 M1 U return Ret; * d0 v& N" y& p% U1 |} 5 R/ K% v; G' C! b: X# V7 a+ e 可以看出基本方法是:( Z6 j' ~. [6 G9 z* w 先检查父目录是否存在,这里用到的函数DirectoryExistEx可以按照前面介绍的方法设计; 7 W1 A7 [: D! B% s如果父目录存在,则直接创建目录,否则自我调用创建父目录。

4 b: s( P* F/ m8 D3 Z

( R0 X; U$ J# K; S ~* z 四、直接删除整个目录:

. x5 W x3 F, F8 b0 s; B; M" g- B$ x

在DOS下有一个Deltree命令,用来删除整个目录,这是一个很有用的功能,可惜,Windows API提供的函数RemoveDirectory只能删除控目录,就像DOS的RD命令一样。编程实现这个功能同样需要递归方法,基本流程是:

) C c, A5 f; h; p) H- R- z

查找目录下的所有文件和目录,即调用API函数FindFirstFile、FindNextFile(*.*) 8 g$ _8 B( e/ P4 r3 U! T如果找到文件,则强制删除。所谓强制删除,即删除前先调用SetFileAttributes把它的属性设置为Normal,然后调用DeleteFile删除它。 & c, [. e H. I x/ s# S5 e如果找到目录,则进行自我调用,即开始递归过程。 7 m% N, N- |" a& o3 ?- H如果没有找到目录,即表示为控目录,调用RemoveDirectory直接删除。 - K o7 f; `1 f# x+ L9 J9 R8 a 具体程序代码如下:

- j1 @1 E4 E) K% }0 P4 @ M

bool DeleteDirectoryEx(const AnsiString &)# w1 L0 p& Q. N: r0 \ {8 ?7 D% @9 f' E3 k& L% O if(P.IsEmpty() || P.Length()<4)return false; // 参数长度必须大于3,即不能为磁盘根目录或空白6 u2 Q* X$ \/ g" A! F int len=P.Length(); % X! _4 |6 K$ F+ ^; H6 ] char *Path=P.c_str(); 7 u$ `: j. c3 C: @" O Z AnsiString Dir=Path; 2 ~: }+ G! M' }+ M4 | if(Path[len-1]!='\\')Dir=Dir+'\\'; 9 z- r# Y1 ^" K3 _. ` F+ ] AnsiString Files=Dir+"*.*";# Q6 C/ c" [( p- w7 I8 {7 L5 M WIN32_FIND_DATA wfd; % D @ c9 g' y; ]( P# H8 J HANDLE hFind=FindFirstFile(Files.c_str(),&wfd); $ o I, y% G' Y) N( R/ v/ G bool Ret=true; ( w% `: z6 M' K" T p( C" S. V+ H AnsiString Tmp; / v% m9 T2 y* s4 M6 z if(hFind!=INVALID_HANDLE_VALUE) / E* F( K( R/ X$ N {; N. \/ B9 z1 U, G bool bFind=true; , m4 ~2 O4 e) i while(bFind); M. U5 J% V6 y/ Q6 h' t {5 g0 [2 ]8 o2 c6 h& `7 j& R( G if(wfd.cFileName[0]!='.') // . ..: z! @& p& [: p) o( b { % a! a. }0 G+ ~$ ~$ b) \ Tmp=Dir+wfd.cFileName;) m* Y+ L) C7 R2 b if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ( y+ V: |6 q6 V7 @ { // 删除所有子目录$ i3 A6 {; c( r1 s Ret=Ret&&DeleteDirectoryEx(Tmp.c_str(),false);5 `9 H6 s4 L( f }else" X3 z; ~$ {: @/ @& w; M { // 删除所有文件 : _/ G. j8 @1 g0 d# \. H& F- J SetFileAttributes(Tmp.c_str(),FILE_ATTRIBUTE_NORMAL); ( }( k; N" A" {3 {1 W2 t# u1 U Ret=Ret&&DeleteFile(Tmp.c_str());, w& q* ?, J; `/ j6 { }8 T1 g1 P8 @8 w a, e } 7 ?* d4 v# U$ R0 C bFind=FindNextFile(hFind,&wfd); 7 N; [* H% Z* Q/ l9 f" h }6 R" Q5 ]9 {& N8 [; q4 Y& n2 ~% k5 t FindClose(hFind);7 m7 j% Z4 ^0 g% A g6 X" W- ? } u! c9 Z6 d* j$ Z, W0 _1 o& }' P% u if(Ret)return RemoveDirectory(Path);' o: S9 o: b! q! D8 ~ return false; - x) V. `( T- b, I1 N$ ~' f: B} 1 P+ M. g$ |9 a% d4 a

& t2 I8 y- ?4 {0 z

+ I. N$ f4 y8 U/ y






欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5