Tach,
eigentlich ist das Problem trivial aber ich habe einen Seiteneffekt, bei dem ich nicht weiß wie ich das geschickt lösen kann.
public void traverseDirsAndRemoveEmpty(DirectoryInfo dinfo, List<Dir> dirList)
{
foreach (DirectoryInfo dSubInfo in dinfo.GetDirectories())
{
traverseDirsAndRemoveEmpty(dSubInfo, dirList);
}
if (dinfo.GetFiles().Length == 0)
{
Dir dir = new Dir();
dir.Dir_fullname = dinfo.FullName;
dir.Dir_name = dinfo.Name;
dirList.Add(dir);
}
}
Wenn ich das auf diese Verzeichnisstruktur anwende:
C:\hans\peter\johannes
C:\hans\eugen\heinz\gustav\max
C:\hans\1\1.1\x.jpg
C:\hans\2\2.1\x.jpg
Ergibt sich das Problem, dass er im zweiten Teil beim Verzeichnis X abbricht, da es hier Dateien gibt. Das aber bedeutet das er den kompletten Pfad z. B. C:\hans\1\1.1 nicht auflisten darf, da diese ja somit nicht leer sind
Irgendwelche Vorschläge?
Hallo MrFluffy,
was möchtest du denn machen?
Alle Verzeichnisse löschen, die leer sind? Wieso willst du dann die mit den Files auflisten?
Gruss
nitro
===
EDIT:
In etwa so? (ungetestet)
public static void TraverseFileSystem(String strDir, List<string> dirList)
{
try
{
string[] a = Directory.GetDirectories(strDir);
// Verzeichnis hat Unterverzeichnisse
if (a.Length > 0)
{
foreach (String strSubDir in Directory.GetDirectories(strDir))
{
TraverseFileSystem(strSubDir, dirList);
}
}
// Verzeichnis ist leer
else
{
dirList.Add(strDir);
}
}
catch (Exception)
{
// 3. Statt Console.WriteLine hier die gewünschte Aktion
Console.WriteLine("error: " + strDir);
}
}
abgeschrieben von
[Snippet] Verzeichnisse und Dateien rekursiv durchlaufen
Gruss
nitro
was möchtest du denn machen?
Alle Verzeichnisse löschen, die leer sind? Wieso willst du dann die mit den Files auflisten?
Ich möchte alle Verzeichnisebenen ausgeben die leer sind. Um leer zu sein dürfen keine Dateien enthalten sein. Außerdem muss logischerweise der komplette Verzeichnisbaum durchlaufen werden.
Das Problem ist am Ende des Durchlaufs gibt es zwei Fälle
a) Im letzten Subordner sind Dateien enthalten
b) Im letzten Subordner sind keine Dateien enthalten
Im Fall a) darf der komplette Baum bis zum letzten Subordner nicht angezeigt werden
Im Fall b) darf der komplette Baum gelöscht werden
In etwa so?
Rein logisch betrachtet, verstösst dieser Code gegen das "leer" Kriterium im Else Zweig können nämlich noch Dateien sein.
Wenn man diesen Fall abfängt, werden die letzten leeren Subordner folglich angezeigt.
Was man nun tun könnte wäre das Pferd von hinten aufzuspannen, d. h. solange im
Pfad zurückspringen bis entweder das "leer" Kriterium verletzt wird oder wir uns im Startpfad befinden.
Da ich ohnehin schon eine gigantische Ordnerstruktur habe auf das ich das ganze anwenden möchte. Wäre es sehr blöd wenn mir der Stack um die Ohren fliegt.
Deshalb frage ich mich ob das nicht einfacher geht.
Zur Veranschaulichung habe ich mal meinen Code sinngemäß angepasst und die Dateien berrücksichtigt
public void traverseDirsAndRemoveEmpty(DirectoryInfo dinfo, List<Dir> dirList)
{
if (dinfo.GetDirectories().Length > 0)
{
foreach (DirectoryInfo dSubInfo in dinfo.GetDirectories())
{
traverseDirsAndRemoveEmpty(dSubInfo, dirList);
}
}
else
{
if (dinfo.GetFiles().Length == 0)
{
Dir dir = new Dir();
dir.Dir_fullname = dinfo.FullName;
dir.Dir_name = dinfo.Name;
dirList.Add(dir);
}
}
Nochmals zu Verdeutlichung
hans\franz\dampf\terminkalender.xls -> Verstoss gegen das Leer Kriterium, da dampf nicht leer ist
peter\maier\sigi\bla\ -> Verzeichnis bla ist leer, wenn Parant Dir ebenfalls leer ist kann es auch entfernt werden und dessen Parent Dir bis zum Startdir
wuff\bg\fdg\fgdfg\
und wuff\bg\file.xls
und wuff\bg\file2.xls
-> Verzeichnis fgdfg ist leer, fdg ist leer, bg beinhaltet Dateien es ist nicht leer -> Abbruch
Hallo MrFluffy,
ich hab mich mit der Problemstellung nicht befasst, aber
Da ich ohnehin schon eine gigantische Ordnerstruktur habe auf das ich das ganze anwenden möchte. Wäre es sehr blöd wenn mir der Stack um die Ohren fliegt.
Statt der Rekursion kann auch ein Stack<T> verwendet werden und somit gehts du der StackOverflowException aus dem Weg.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo MrFluffy,
du rufst ja die Procedure rekursiv auf, um von einer Verzeichniswurzel in die Tiefe zu gehen. Was fehlt ist die Rückmeldung ob eine Datei gefunden wurde, so dass du für das Parentverzeichnis abschließend entscheiden kannst, ob es in Liste der "leeren Verzeichnisse" mit aufgenommen werden kann.
Zudem sollte immer - also unabhängig vor der Existenz von Subdirs - geprüft werden, ob eine Datei vorliegt.
Folgende Möglichkeiten bestehen:
Hier mal Ansatz Nr. 3:
public bool traverseDirsAndRemoveEmpty(DirectoryInfo dinfo, List<Dir> dirList)
{
bool FileFound = false;
if (dinfo.GetDirectories().Length > 0)
{
foreach (DirectoryInfo dSubInfo in dinfo.GetDirectories())
{
FileFound = FileFound | traverseDirsAndRemoveEmpty(dSubInfo, dirList);
}
}
if (!FileFound)
{
if (dinfo.GetFiles().Length == 0)
{
Dir dir = new Dir();
dir.Dir_fullname = dinfo.FullName;
dir.Dir_name = dinfo.Name;
dirList.Add(dir);
return false;
}
}
return true;
}
Nicht getestet!
Gruß Dani
Statt der Rekursion kann auch ein Stack<T> verwendet werden und somit gehts du der StackOverflowException aus dem Weg.
Stimmt, jetzt wo Du es sagst 😉
Danke für den Hinweis
du rufst ja die Procedure rekursiv auf, um von einer Verzeichniswurzel in die Tiefe zu gehen. Was fehlt ist die Rückmeldung ob eine Datei gefunden wurde, so dass du für das Parentverzeichnis abschließend entscheiden kannst, ob es in Liste der "leeren Verzeichnisse" mit aufgenommen werden kann.
Genau 😉
ich muss sagen sehr elegante Lösung, insbesondere die Verwendung des Bit Or die ich noch nie verwendete übt eine gewisse Faszination auf mich aus.
So einfach und so effektiv 👍
Hatte ein schönes "AHA" Erlebnis, vielen Dank dafür
Hi,
auf die Variable kann man sogar verzichten, da man mit
if (traverseDirsAndRemoveEmpty(dSubInfo, dirList))
return true;
einfach aus der Schleife springen kann, sobald man ein nicht-leeres Verzeichnis gefunden hat.
Außerdem würde ich die Abfrage auf die Dateien zuerst durchführen, damit man nicht erst die Unterordner iterieren muss.
Hi Cat,
auf die Variable kann man sogar verzichten, da man ... einfach aus der Schleife springen kann, sobald man ein nicht-leeres Verzeichnis gefunden hat.
Außerdem würde ich die Abfrage auf die Dateien zuerst durchführen, damit man nicht erst die Unterordner iterieren muss.
Wenn du bei der erstbesten Gelegenheit herausspringst, hast du aber nicht untersucht, ob es noch "leere" Verzeichnisse in der tieferen Struktur gibt. Dann dürfte das Listing aber nur sehr kurz und unvollständig sein.
Gruß Dani
Hast Recht - ich hatte die Aufgabenstellung nicht genau genug durchgelesen.
Oder um den nicht-rekursiven Ansatz von Günni weiterzuverfolgen:
public void getDirectories(String path, List<String> dirList)
{
directoryStack.Push(path);
while (directoryStack.Count > 0)
{
String currentDirectory = directoryStack.Pop();
foreach (String successorDirectory in Directory.GetDirectories(currentDirectory))
{
directoryStack.Push(successorDirectory);
}
if (Directory.GetFiles(currentDirectory).Length == 0
&& Directory.GetDirectories(currentDirectory).Length == 0)
{
dirList.Add(currentDirectory);
String predecessorDirectory = currentDirectory;
bool validParent = true;
while (Directory.GetFiles(predecessorDirectory).Length == 0 && validParent)
{
dirList.Add(predecessorDirectory);
List<DirectoryInfo> nextParent = Directory.GetParent(predecessorDirectory).GetDirectories().ToList();
int i = nextParent.FindAll((x) =>
{
if (dirList.Contains(x.FullName) | x.GetFiles().Length == 0)
{
return true;
}
else
{
return false;
}
}
).Count;
if (i == nextParent.Count)
{
predecessorDirectory = Directory.GetParent(predecessorDirectory).FullName;
}
else
{
validParent = false;
}
}
}
}
}
Der Hauptvorteil von nicht-rekursiven Methoden ist, dass sie oft deutlich inituitiver sind als ihre rekursiven Pendants.
Ich versuche eigentlich immer, alle rekursiven Methoden sofort in iterative Methoden umzubauen, damit andere Entwickler den Code leichter verstehen.
q.e.d.
Hallo,
Der Hauptvorteil von nicht-rekursiven Methoden ist, dass sie oft deutlich inituitiver sind als ihre rekursiven Pendants.
Das kann auch andersrum sein. Üblicherweise werden die rekursiven Varianten als inituitiver angesehen. Aber das wurde im Forum schon diskutiert. Siehe Rekursion grundsätzlich langsamer? und Gibt es Rekursionen die sich nicht in eine Iteration umwandeln lassen?. Von daher bitte hier keine Diskussion darüber starten.
Der Hauptvorteil ist dass keine StackoverflowException auftritt.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
int i = nextParent.FindAll((x) => { if (dirList.Contains(x.FullName) | x.GetFiles().Length == 0) { return true; } else { return false; } } ).Count;
Kleine Rückfrage diesbezüglich. Wie nennt man dieses Sprachkonstrukt mit dem => Operator in der C# Welt?
Da er mir nicht geläufig ist wollte ich mir diesbezüglich gerne Informationen einholen, aber das ist etwas schwierig ohne das passende Stichwort.
Danke
Hallo MrFluffy,
Lambda-Operator. Siehe auch LinQ und Lambda-Ausdrücke.
In Visual Studio, falls du das hast, F1 drücken wirkt oft Wunder 😉
Das Zitierte ist übrigens was für Coding-Style-Horror, da es viel einfacher und kürzer geht:
if (dirList.Contains(x.FullName) | x.GetFiles().Length == 0)
{
return true;
}
else
{
return false;
}
entspricht
return (dirList.Contains(x.FullName) || x.GetFiles().Length == 0); // das OR hab ich auch noch Shortcut gemacht
Siehe auch [Tipp] Anfängerfehler == true / == false
Und insgesamt kann das noch verkürzt werden zu:
int i = nexParent.Count(x => dirList.Contains(x.FullName) || x.GetFiles().Length == 0);
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo zusammen,
Statt der Rekursion kann auch ein Stack<T> verwendet werden und somit gehts du der StackOverflowException aus dem Weg.
die maximale Rekursionstiefe beim Durchsuchen einer Verzeichnisstruktur entspricht - vom Startverzeichnis aus gemessen - der maximalen Tiefe der Verzeichnisstruktur (mit anderen Worten der Anzahl der Verzeichnisse im längsten (relativen) Pfad, wobei die Länge hier nicht in Anzahl Zeichen, sondern in Anzahl Verzeichnisse gemessen wird). Die Breite der Verzeichnisstruktur und die Gesamtzahl der zu durchsuchenden Verzeichnisse spielen für den Stackverbrauch überhaupt keine Rolle. Üblicherweise beträgt die Tiefe selbst bei umfangreichen Verzeichnisstrukturen nicht mehr als 10. Ich habe noch nie eine Verzeichnisstruktur tiefer als 20 gesehen. Aber selbst wenn man pessimistisch von einer Tiefe von maximal 100 oder sogar 1000 ausgeht, ist man von einer StackOverflowException Lichtjahre entfernt. Es gibt daher keinen Grund, eine nicht rekursive Implementierung zu verwenden, um eine StackOverflowException zu vermeiden.
Selbst wenn man bis zum theoretischen Maximum geht (bei NTFS ist die maximale Länge eines Dateipfades 32767 Zeichen. Jedes Verzeichnis verbraucht zwei Zeichen, eins für den Namen, eins fürs Trennzeichen. Die maximale Rekursionstiefe beträgt also 16383) ist man von einer StackOverflowException weit entfernt. Danke an MarsStein für diese ergänzendes Berechnung.
herbivore