Beschreibung:
mit .NET 2.0 wurde die Methode
Directory.GetFiles (strStartVerzeichnis, "*", SearchOption.AllDirectories)
eingeführt, die durch die Angabe SearchOption.AllDirectories nicht nur die Dateien in strStartVerzeichnis liefert, sondern auch rekursiv alle Dateien in allen Unterverzeichnissen. Leider liefert die Methode kein Ergebnis sondern eine Exception, sobald auch nur auf ein Unterverzeichnis kein Zugriff besteht. Deshalb ist die Methode in der Praxis nur sehr eingeschränkt nutzbar und man muss die Rekursion doch selbst ausprogrammieren.
Um die mit .NET 4.0 eingeführte Methode Directory.EnumerateFiles steht es in Bezug auf SearchOption.AllDirectories nicht viel besser, wie weiter unten im Thread diskutiert wird.
Natürlich gibt es noch andere Gründe, auf eine eigene rekursive Methode zurückzugreifen, z.B. wenn man das Dateisystem in einer bestimmten Reihenfolge durchlaufen will oder man nur in bestimmte Unterverzeichnisse berücksichtigen will oder nur Unterverzeichnisse bis zu einer bestimmten Tiefe.
Deshalb hier ein Rahmen für eine solche, rekursive Methode, den man nach eigenem Gutdünken ausfüllen kann:
public static void TraverseFileSystem (String strDir) {
try {
// 1. Für alle Dateien im aktuellen Verzeichnis
foreach (String strFile in Directory.GetFiles (strDir)) {
// 1a. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine (strFile);
}
// 2. Für alle Unterverzeichnisse im aktuellen Verzeichnis
foreach (String strSubDir in Directory.GetDirectories (strDir)) {
// 2a. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine (strSubDir);
// 2b. Rekursiver Abstieg
TraverseFileSystem (strSubDir);
}
}
catch (Exception) {
// 3. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine ("error: " + strDir);
}
}
**
Anmerkungen:**
Directory.GetFiles (strDir)
und/oder Directory.GetDirectories (strDir)
kann man vor dem Durchlaufen in jede gewünschte Reihenfolge bringen. Klassisch z.B. mit Array.Sort und einem eigenen IComparer<T> oder eleganter per Linq mit OrderBy direkt im Schleifenkopf.Directory.GetFiles/GetDirectories
vor dem Schleifenkopf aufrufen und das Ergebnis in einer Variable zwischenspeichern.
// 1. Für alle Dateien im aktuellen Verzeichnis
foreach (FileInfo fi in new DirectoryInfo (strDir).GetFiles ()) {
// 1a. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine (fi.FullName);
}
**
Variante mit FileInfo und DirectoryInfo**
Hier folgt noch eine Variante, bei der statt der Klassen File und Directory die Klassen FileInfo und DirectoryInfo verwendet werden. Das hat den Vorteil, dass man gleich die meisten Informationen über Dateien und Verzeichnisse parat hat, z.B. das Datum des letzten Zugriffs. Auf der anderen Seite müssen diese Informationen ja ermittelt und in die Objekte übertragen werden. Dadurch sind Laufzeit und Speicherverbrauch etwas größer. Zumindest auf lokalen Platten sind die Unterschiede praktisch so gering, dass es im Wesentlichen Geschmackssache ist, für welche Variante man sich entscheidet.
Die Anmerkungen von oben gelten für diese Variante genauso.
public static void TraverseFileSystem (String strDir) {
// 0. Einstieg in die Rekursion auf oberster Ebene
TraverseFileSystem (new DirectoryInfo (strDir));
}
private static void TraverseFileSystem (DirectoryInfo di) {
try {
// 1. Für alle Dateien im aktuellen Verzeichnis
foreach (FileInfo fi in di.GetFiles ()) {
// 1a. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine (fi.FullName);
}
// 2. Für alle Unterverzeichnisse im aktuellen Verzeichnis
foreach (DirectoryInfo diSub in di.GetDirectories ()) {
// 2a. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine (diSub.FullName);
// 2b. Rekursiver Abstieg
TraverseFileSystem (diSub);
}
}
catch (Exception) {
// 3. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine ("error: " + di.FullName);
}
}
**
Siehe auch**
MaskMatch Klasse zum durchsuchen mehrerer Verzeichnisebenen
Schlagwörter: Datei, Dateien, Dateiname, Dateinamen, File, Files, Filename, Filenames, File Name, File Names, Verzeichnis, Verzeichnisse, Verzeichnisname, Verzeichnisnamen, Directory, Directories, Directoryname, Directorynames, Directory Name, Directory Names, Unterverzeichnis, Unterverzeichnisse, Subdirectory, Subdirectories, Dateisystem, Filesystem, File System, FAT, FAT32, NTFS rekursiv, rekursive, rekursiven, rekursives, Rekursion, recusive, durchlaufen, traversieren, auflisten, aufzählen, suchen, durchsuchen, Ereignis, Ereignisse, Event, Events, ereignisbasiert, ereignisbasierte, ereignisorientiert, ereignisorientierte, ereignisgesteuert, ereignisgesteuerte, List, Lists, Liste, Listen, Reihenfolge, sortiert, sortierte, sortieren, Sortierung, 1000 Worte
Mit Verknüpfung in dem folgenden MSDN-Zitat sind wohl keine .lnk-Dateien, sondern Hardlinks oder Junktions gemeint.
Es gibt noch einen anderen Grund, der gegen die AllDirectories-Methode spricht:
Hinweis:
Wenn Sie AllDirectories in der Suche auswählen und die Verzeichnisstruktur eine Verknüpfung enthält, sodass eine Schleife entsteht, befindet sich der Suchvorgang in einer Endlosschleife.
Würde es denn deiner Meinung nach große Performanceeinbußen geben, wenn man die Aktionen bei deinem Code als Delegates definieren würde?
Dann hätte man eine allgemeingültige Methode (oder 2, wenn man die Reihenfolge der beiden Aktionen 1 und 2 vertauscht), ohne immer wieder den Code kopieren zu müssen.
das mit dem delegate ist gut möglich nur nicht sinnvoll. man kann ja nicht wissen, was der entwickler machen möchte.
übrigens hätte man bei dieser methode ebenfalls ein problem mit verknüpfungen auf einem ordner. der ausweg ist, alle bereits durchlaufenen ordner zu merken und ggf. nicht mehr zu durchlaufen. aber das ist auch eine anforderung, die nicht oft benötigt wird und ist aus diesem grund auch niht in der frameworkversion enthalten.
Hallo Th69,
Würde es denn deiner Meinung nach große Performanceeinbußen geben, wenn man die Aktionen bei deinem Code als Delegates definieren würde?
nein, das aufwändige sind die Festplattenzugriffe. Ein zusätzlicher Delegaten-Aufruf pro Datei sollte nicht wirklich was ändern. Das was du meinst, kann man also durchaus machen, auch wenn ich für Events statt "einfachen" Delegaten plädiere. Es war nur nicht mein Ziel mit diesem Snippet. Ich sehe das Snippet als Vorlage für eine eigene, individuell zu schreibende Methode. Daher auch die Anmerkungen, welche Änderungsmöglichkeiten bestehen. Ich habe deine Anregung aber oben als eine weitere Änderungsmöglichkeit aufgenommen.
Wenn Sie AllDirectories in der Suche auswählen und die Verzeichnisstruktur eine Verknüpfung enthält, sodass eine Schleife entsteht, befindet sich der Suchvorgang in einer Endlosschleife.
Mit Verknüpfung wird hier wohl nicht nicht Verküpfung im Sinne von .lnk-Dateien gemeint sein, sondern Hardlinks. Und wer Hardlinks auf übergeordnete Verzeichnisse anlegt, gehört erschossen 🙂 Im Sinne der Robustheit wäre es jedoch sinnvoll, wenn ein Programm damit umgehen kann. Leider bleibt einem dazu wohl wirklich nichts anders, als was JAck30lena vorgeschlagen hat. [EDIT]Am besten mit dem Vorschlag von winSharp93 weiter unten.[/EDIT]
herbivore
Wusste garnicht das bei NTFS Hardlinks möglich sind. Wieder was gelernt 😄
NTFS unterstützt sogar das hart verlinken von Verzeichnissen. Unter http://www.mcseboard.de/tipps-links-5/versteckte-funktionen-ntfs-hardlinks-u-abzweigungspunkte-58903.html gibt es eine sehr schöne kleine Einführung in das Thema
Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...
Hallo zusammen,
auch wenn der Thread nicht gerade brandneu ist:
Leider bleibt einem dazu wohl wirklich nichts anders, als was JAck30lena vorgeschlagen hat.
Nein - es geht auch einfacher.
Zuerst kann man prüfen, ob ein Verzeichnis eine Junction ist:
DirectoryInfo info = new DirectoryInfo("/**/");
if ((info.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
{ //Es handelt sich um eine Junction
}
Dann hilft Codeproject: Manipulation NTFS Junctions weiter. Der Artikel stellt eine Library vor, mit der man das Ziel einer Junction ermitteln kann. Hat man dieses erst einmal, muss man nur noch prüfen, ob die Junction auf ein übergeordnetes Verzeichnis zeigt.
Leider fehlt mir aber im Moment die Zeit, das ganze wirklich zu testen und einen ordentlichen Beispielcode zu schreiben. Die Idee wollte ich aber trotzdem hier niederschreiben.
Hallo,
ich habe gerade lange Zeit mich über die besch... Implementierung von GetFiles() geärgert, da bin ich auf diese neue Methode gestoßen: EnumerateFiles(String, SearchOption) (ab .NET4) 😁
Gruß mru.
Hallo mru,
erstmal danke für den Hinweis. Leider wird durch die neuen Methoden das eigentliche Problem nicht gelöst. Aber der Reihe nach.
Es gibt sowohl die Methode DirectoryInfo.EnumerateFiles als auch die Methode Directory.EnumerateFiles.
In der MSDN-Doku steht bei beiden Methoden zu lesen:
Die EnumerateFiles-Methode und die GetFiles-Methode unterscheiden sich wie folgt: Wenn Sie EnumerateFiles verwenden, können Sie anfangen, die Auflistung von Namen aufzulisten, bevor die ganze Auflistung zurückgegeben wird; wenn Sie GetFiles verwenden, müssen Sie warten, bis das ganze Array von Namen zurückgegeben wird, bevor Sie auf das Array zugreifen können. Wenn Sie daher mit vielen Dateien und Verzeichnissen arbeiten, kann EnumerateFiles effizienter sein.
Der Unterschied besteht also nur darin, ob die Ergebnisse auf einen Rutsch oder Datei für Datei geliefert werden. Das eigentliche Problem beim rekursiven GetFiles ist aber ein anderes. Ich habe es ganz oben beschrieben:
Leider liefert die Methode kein Ergebnis sondern eine Exception, sobald auch nur auf ein Unterverzeichnis kein Zugriff besteht.
Und dieses Problem löst auch EnumerateFiles nicht wirklich. Zwar kommt die Exception nicht sofort, sondern erst wenn das "böse" Verzeichnis erreicht ist (vielen Dank an gfoidl, der das getestet hat), es bleibt aber das Problem, dass die Auflistung mit einer Exception abbricht, sobald auch nur auf ein Unterverzeichnis kein Zugriff besteht. Insofern ist auch EnumerateFiles keine echte Lösung.
Aber selbst wenn auf alle zu durchsuchenden Verzeichnisse Zugriff besteht, hat man bei EnumerateFiles zumindest keinen Einfluss auf die Reihenfolge, in der die Dateien aufgelistet werden. Wenn man mein rekursives Snippet verwendet, kann man durch die im Text beschriebenen Änderungen die Reihenfolge den eigenen Vorstellungen und Anforderungen anpassen.
Und auch wenn man Dateien und Verzeichnisse gleichzeitig/verzahnt auflisten will, helfen weder die rekursiven GetFiles/EnumerateFiles noch die rekursiven GetDirectories/EnumerateDiectories alleine, sondern nur eine eigene rekursive Implementierung entsprechend des obigen Snippets.
EnumerateFiles bringt nur dann spürbare Vorteile bringt, wenn man wiederholt vor hat, die Enumeration abzubrechen, bevor alle Dateien durchlaufen sind. Oder wenn man Verzeichnisse mit extrem vielen Dateien hat. Ansonsten kann man weiterhin GetFiles verwenden. GetFiles hat aus meiner Sicht den Vorteil, dass es das komplette Ergebnis liefert oder gar keins (SecurityException). Bei EnumerateFiles können Exceptions wie gesagt mitten in der Enumeration auftreten. Und GetFiles ist sowieso angezeigt, wenn man die Dateien vor der Verarbeitung sortieren will oder muss. Es kommt also auf die konkrete Situation an, ob die eine oder die andere Methode besser geeignet ist.
Das Gesagte gilt für EnumerateDirectories analog.
herbivore
Hallo zurück,
in der Tat. Ich habe das eben durchexerziert und beim Erreichen des "System Volume Information" Verzeichnisse bricht es in der Tat ab.
Ich verstehe nicht, warum MS es nicht gelingt, diese Methode als Option ohne Exception zu implementieren und man dies selbst machen muss 😦
Gruß mru
Themen zusammengefügt
Ich glaube so etwas gibt es hier noch nicht, falls doch: sorry, mein fehler...
Beschreibung:
Per
var allfiles = System.IO.Directory.GetFiles(@"C:\MyDirectory", "*.*", System.IO.SearchOption.AllDirectories);
kann man sich ja ganz einfach alle Dateien eines Ordners samt der Unterordner anzeigen lassen, jedoch gibt es ein Problem, falls dort ein Ordner sein solle, für den man keine Zugriffsrechte besitzt. In diesem Fall wird eine
UnauthorizedAccessException
geworfen und keine weiteren Dateien mehr ausgegeben.
Für diesen Fall hab ich eine kleine Rekursive Methode entwickelt.
Einfach mit
List<string> files = GetAllAccessibleFiles(@"C:\MyDirectory");
aufrufen.
Snippet:
public static List<string> GetAllAccessibleFiles(string rootPath, List<string> alreadyFound = null)
{
if (alreadyFound == null)
alreadyFound = new List<string>();
DirectoryInfo di = new DirectoryInfo(rootPath);
var dirs = di.EnumerateDirectories();
foreach (DirectoryInfo dir in dirs)
{
if (!((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden))
{
alreadyFound = GetAllAccessibleFiles(dir.FullName, alreadyFound);
}
}
var files = Directory.GetFiles(rootPath);
foreach (string s in files)
{
alreadyFound.Add(s);
}
return alreadyFound;
}
Schlagwörter: rekursiv dateien ordner unterordner auflisten UnauthorizedAccessException
Hallo zusammen,
dadurch, dass in dem Snippet von emsch der rekursive Abstieg zuerst erfolgt, landen die Dateien in einer unüblichen Reihenfolge in der Liste (bottom first). Wenn man das Füllen der Liste und den rekursiven Abstieg vertauscht, entspricht die Reihenfolge dem gewohnten Bild.
EDIT: Außerdem ist es inkonsequent, dass versteckte Ordner (und ihre Unterordner) nicht durchlaufen werden, aber versteckte Dateien in den durchlaufenen Ordnern aufgelistet werden. Vermutlich liegt die Ursache in der von Th69 im folgenden Beitrag angesprochenen Verwechslung.
BTW: Die Zuweisung an alreadyFound bei rekursiven Abstieg ist nicht erforderlich.
herbivore
Hallo herbivore, hallo emsch,
die Abfrage auf versteckte Dateien hat doch ersteinmal nichts direkt mit den Zugriffsrechten zu tun (auch wenn einige Systemordner als versteckt markiert sind), daher sollte man zusätzlich immer noch diese Exception abfangen.