Jeg startede min søgen i IOUtils.pas, som kom i Delphi 2009. Helt konkret kiggede jeg på TDirectory, hvori jeg forventede at finde noget der kunne bruges til formålet. Det var nu ikke tilfældet, så måtte jeg skrive en selv.
Det vil dette blogindlæg handle om.
Først skal jeg have sat en test case op (endnu et fint dansk ord ;o)). Jeg har lavet den følgende mappestruktur i roden af mit D-drev:
Mappenavnene fortæller om eventuelt indhold.
Jeg ønsker at bruge den følgende syntaks: TDirectory.DeleteEmptyDirectories('D:\Test');
Primært fordi jeg synes metoden logisk hører hjemme på TDirectory.
Type definitionen kommer til at se sådan ud:
På TDirectory er der nogle metoder jeg vil benytte mig af :uses IOUtils, Types; type TTDirectoryHelper = record helper for TDirectory public class function DeleteEmptyDirectories ( const aRoot: string; aSearchPattern: string = '*'; aSearchOption: TSearchOption = TSearchOption.soAllDirectories ): TStringDynArray; static; end;
- TDirectory.Exists()
Skal bruges til at tjekke eksistensen af en given sti - TDirectory.GetDirectories()
Skal bruges til at hente alle under mapper i forhold til en given sti. - TDirectory.IsEmpty()
Skal bruges til at tjekke om en folder er tom eller ej - TDirectory.Delete()
Sletter en mappe, samt eventuelle under mapper
Metoden er meget kort fortalt, at løbe en mappe med under mapper igennem. Det kan gøres med TDirectory.GetDirectories(aRoot, aSearchPattern, aSearchOption). For hver af disse mapper skal man tjekke om de eventuelt er tomme, og i så fald slette dem. Hvis man støder på en mappe som ikke er tom, skal denne føjes til en liste så man undgår et uendeligt loop.
Personligt kan jeg nemt miste overblikket over en algoritme, hvis den bliver penslet ud i tekst. Så det vil jeg undlade her, blot vise koden i sin fulde udstrækning, og så i øvrigt henvise til kommentarene i koden.
unit FileAndFolderUtilsU;
interface uses IOUtils, Types; typeimplementation uses Sysutils, Classes; { TTDirectoryHelper }TTDirectoryHelper = record helper for TDirectory public class function DeleteEmptyDirectories ( const aRoot: string; aSearchPattern: string = '*'; aSearchOption: TSearchOption = TSearchOption.soAllDirectories ): TStringDynArray; static; end;class function TTDirectoryHelper.DeleteEmptyDirectories( const aRoot: string; aSearchPattern: string = '*'; aSearchOption: TSearchOption = TSearchOption.soAllDirectories ): TStringDynArray; static;var Directory, Tmp: string; Directories: TStringDynArray; Stop: Boolean; DirBuffer: TStringList; begin // Hvis det ikke en en gyldig sti, så exit if not TDirectory.Exists(aRoot) then exit; DirBuffer := TStringList.Create; { DirDuffer skal bruges til at samle alle de mapper op,
som enten ikke er tomme, eller det ikke lykkes at slette.
Dette gøres fordi det skal bruges en "stop kondition",
så vi undgår uendeligt loop: Hvis de foldere der er tilbage
i vores struktur alle findes i listen, så er der ikke mere
vi kan gøre. } try Directories := TDirectory.GetDirectories(
aRoot, aSearchPattern, aSearchOption);
Stop := Length(Directories) = 0; if Stop then TDirectory.Delete(aRoot); { Hent en liste over alle subfolders. Hvis der ingen er, så slet "dig selv" og forlad funktionen } while not Stop do begin for Directory in Directories do if TDirectory.Exists(Directory) then { Det er nødvendigt at tjekke på mappens fortsatte eksistens idet funktionen her er rekursiv, og mappen kan være slettet i en af de andre rekursioner. } if TDirectory.IsEmpty(Directory) then begin TDirectory.Delete(Directory, True); //Slet den tomme folder. { Hvis den overordnet mappe nu er tom, skal den slettes. Det gøres ved at kalde funktionen igen, så herved slettes eventuelle mapper som NU er tomme som følge af den nylige
slettede mappe. } DeleteEmptyDirectories(aRoot, aSearchPattern, aSearchOption); if TDirectory.Exists(Directory) then DirBuffer.Add(Directory); { Hvis folderen fortsat eksisterer efter seltning, er det fordi
den er låst af en anden process. I det tilfælde tilføjes den
bare på DirBuffer. } end else DirBuffer.Add(Directory); //Hvis mappen ikke er tom skal den også bare tilføjes til DirBuffer. Directories := TDirectory.GetDirectories(
aRoot, aSearchPattern, aSearchOption); //Hent en liste over alle de foldere der nu er tilbage Stop := True; for Directory in Directories do Stop := Stop and (DirBuffer.IndexOf(Directory) >= 0); //Hvis alle de resterende mapper enten er i brug eller har et indhold, så exit. DirBuffer.Clear; //Ryd DirBuffer til en eventuel ny omgang. end; finally FreeAndNil(DirBuffer); //Oprydning. end; //Returner en liste over de mapper der er tilbage - altså dem
//der ikke kunne slettes (var i brug) eller ikke var tomme. Result := TDirectory.GetDirectories(aRoot, aSearchPattern, aSearchOption); end; end.
Til sidst er der blot tilbage at kalde funktionen:
uses IoUtils, FileAndFolderUtilsU; procedure TForm1.FormCreate(Sender: TObject); begin TDirectory.DeleteEmptyDirectories('D:\Test'); end;
Jens Borrisholt
Uhh, den er let: "find ./ -type d -exec rmdir {} \;" :)
SvarSletJeg ved ikke om man har noget ala symlinks/hardlinks på NTFS - men ellers ser jeg også et potentielt uendeligt loop der.