IO.GetDirectories nur Ordner auflisten, die MP3 Dateien enthalten.

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 32 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    IO.GetDirectories nur Ordner auflisten, die MP3 Dateien enthalten.

    Hi,
    ich versuche gerade alle Ordner aufzulisten, die lediglich MP3 Dateien enthalten, auch für die Unterordner.
    Sodass wenn ich dann auf C: im Treeview drücke, das dann nur diese Ordner samt Unterordner und Dateien angezeigt werden.
    Soweit funktioniert es mit diesem Code:

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. foreach (var dir in Directory.GetDirectories(path))
    6. {
    7. try
    8. {
    9. if (Directory.GetFiles(dir, "*.mp3", SearchOption.AllDirectories).Count() > 0)
    10. items.Add(new DirectoryItem { FullPath = dir, Type = DirectoryItemTypeEnum.Folder });
    11. }
    12. catch (UnauthorizedAccessException)
    13. {
    14. continue;
    15. }
    16. }
    17. foreach (var file in Directory.GetFiles(path, "*.mp3"))
    18. {
    19. items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    20. }
    21. return items;
    22. }




    Allerdings dauert es ewig (ca. 20 bis 30 Sekunden).
    Gibt es eine schnellere, elegantere Möglichkeit?

    Danke Euch
    "Hier könnte Ihre Werbung stehen..."

    MichaHo schrieb:

    C#-Quellcode

    1. if (Directory.GetFiles(dir, "*.mp3", SearchOption.AllDirectories).Count() > 0)
    2. // ...
    3. foreach (var file in Directory.GetFiles(path, "*.mp3"))
    Diese beiden Aufrufe kannst Du vereinen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @MichaHo Lies die Dateien in ein Array und werte das Array zwei Mal aus.
    Feddich.

    C#-Quellcode

    1. var files = Directory.GetFiles(dir, "*.mp3", SearchOption.AllDirectories)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also ich hab es nun mit mehreren Varianten propiert (spoiler) entweder fliegt ne Exception weil ich keinen Zugriff auf den Ordner habe, oder es wird nur 1 Ordner angezeigt, oder alle Ordner.
    Vielleicht nochmal als Pseydocode:

    Hole alle Ordner vom angegebeben Pfad, schaue ob sich darin .mp3 Files befinden.
    Wenn ja, füge der Liste ein neues DirectoryItem hinzu mit Ordner Pfad und TypeEnum.Folder.
    Füge dann der Liste für jedes gefundene File wieder ein neues DirectoryItem hinzu mit Pfad zur Datei und TypeEnum.File.
    wenn nein, gehe zum nächsten Ordner

    Also hinterher soll im TreeView wenn ich zum Beispiel C oder D aufklappe, dort nur die Ordner angezeigt werden, die MP3 Files beinhalten, wenn der Ordner aufgeklappt wird, sollen dort die Files angezeigt werden.

    Gestartet wird die Methode von der Expand Methode im ViewModel:

    C#-Quellcode

    1. private void Expand()
    2. {
    3. if (Type == DirectoryItemTypeEnum.File) return;
    4. var childItems = FullPath.GetFilesFolders();
    5. ChildItems = new ObservableCollection<FolderBrowserItemViewModel>();
    6. childItems.ForEach(f => ChildItems.Add(new FolderBrowserItemViewModel(f)));
    7. }


    Spoiler anzeigen

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. foreach (var dir in Directory.GetDirectories(path))
    6. {
    7. try
    8. {
    9. foreach (var file in Directory.GetFiles(dir, "*.mp3"))
    10. {
    11. if (dir.Contains(file))
    12. items.Add(new DirectoryItem { FullPath = dir, Type = DirectoryItemTypeEnum.Folder });
    13. items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    14. }
    15. }
    16. catch (UnauthorizedAccessException)
    17. {
    18. continue;
    19. }
    20. }
    21. return items;
    22. }
    23. //public static List<DirectoryItem> GetFilesFolders(this string path)
    24. //{
    25. // var items = new List<DirectoryItem>();
    26. // if (!Directory.Exists(path)) return new List<DirectoryItem>();
    27. // foreach (var dir in Directory.GetDirectories(path))
    28. // {
    29. // try
    30. // {
    31. // if (Directory.GetFiles(dir, "*.mp3",SearchOption.AllDirectories).Count() > 0)
    32. // items.Add(new DirectoryItem { FullPath = dir, Type = DirectoryItemTypeEnum.Folder });
    33. // }
    34. // catch (UnauthorizedAccessException)
    35. // {
    36. // continue;
    37. // }
    38. // }
    39. // foreach (var file in Directory.GetFiles(path, "*.mp3"))
    40. // {
    41. // items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    42. // }
    43. // return items;
    44. //}
    45. //public static List<DirectoryItem> GetFilesFolders(this string path)
    46. //{
    47. // var items = new List<DirectoryItem>();
    48. // if (!Directory.Exists(path)) return new List<DirectoryItem>();
    49. // foreach (var folder in new DirectoryInfo(path).GetDirectories().ToList())
    50. // {
    51. // try
    52. // {
    53. // foreach (var file in folder.GetFiles("*.mp3"))
    54. // {
    55. // items.Add(new DirectoryItem { FullPath = folder.FullName, Type = DirectoryItemTypeEnum.Folder });
    56. // items.Add(new DirectoryItem { FullPath = file.FullName, Type = DirectoryItemTypeEnum.File });
    57. // }
    58. // }
    59. // catch (UnauthorizedAccessException)
    60. // {
    61. // continue;
    62. // }
    63. // }
    64. // return items;
    65. //}
    66. //public static List<DirectoryItem> GetFilesFolders(this string path)
    67. //{
    68. // var items = new List<DirectoryItem>();
    69. // if (!Directory.Exists(path)) return new List<DirectoryItem>();
    70. // var folders = new DirectoryInfo(path).GetDirectories().ToList();
    71. // var files = new DirectoryInfo(path).GetFiles("*.mp3").ToList();
    72. // if(folders.Count > 0)
    73. // {
    74. // items.AddRange(folders.Select(f => new DirectoryItem { FullPath = f.FullName, Type = DirectoryItemTypeEnum.Folder }));
    75. // }
    76. // if(files.Count > 0)
    77. // {
    78. // items.AddRange(files.Select(f => new DirectoryItem { FullPath = f.FullName, Type = DirectoryItemTypeEnum.File }));
    79. // }
    80. // return items;
    81. //}
    82. //public static List<DirectoryItem> GetFilesFolders(this string path)
    83. //{
    84. // var items = new List<DirectoryItem>();
    85. // if (!Directory.Exists(path)) return new List<DirectoryItem>();
    86. // var folders = Directory.GetDirectories(path);
    87. // var files = Directory.GetFiles(path);
    88. // if (folders.Length > 0)
    89. // {
    90. // items.AddRange(folders.Select(f => new DirectoryItem
    91. // {
    92. // FullPath = f,
    93. // Type = DirectoryItemTypeEnum.Folder
    94. // }));
    95. // }
    96. // if (files.Length > 0)
    97. // {
    98. // items.AddRange(files.Select(f => new DirectoryItem
    99. // {
    100. // FullPath = f,
    101. // Type = DirectoryItemTypeEnum.File
    102. // }));
    103. // }
    104. // return items;
    105. //}


    "Hier könnte Ihre Werbung stehen..."
    @MichaHo Ich habs nicht probiert, aber müsste das nicht so sein (Klammer um beide Zeilen):

    C#-Quellcode

    1. if (dir.Contains(file))
    2. {
    3. items.Add(new DirectoryItem { FullPath = dir, Type = DirectoryItemTypeEnum.Folder });
    4. items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    5. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Jetzt wirds komisch...

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. foreach (var folder in Directory.GetDirectories(path))
    6. {
    7. try
    8. {
    9. foreach (var file in Directory.GetFiles(folder,"*.mp3"))
    10. {
    11. if(folder.Contains(file))
    12. {
    13. items.Add(new DirectoryItem { FullPath = folder, Type = DirectoryItemTypeEnum.Folder });
    14. items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    15. }
    16. }
    17. }
    18. catch (UnauthorizedAccessException)
    19. {
    20. continue;
    21. }
    22. }
    23. return items;
    24. }


    hab mal nen Haltepunkt auf die Zeile 12 gemacht und bin mal durchgesteppt....
    Im Ordner Desktop habe ich 1 MP3 file, das erkennt er und bleibt beim Haltepunkt stehen - > alles richtig, nun hätte ich erwartet, das er die beiden CodeZeilen ausführt, macht er aber nüscht, er geht einfach weiter und sucht den nächsten Ordner.
    "Hier könnte Ihre Werbung stehen..."
    @MichaHo Muss ich wohl doch ein Testprogramm schreiben ...
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich kann das auch morgen schreiben und lade es dann hoch, dann kann sich jeder austoben...

    Dieses Konstrukt hier funktioniert einwandfrei, es listet alle Ordner auf und zeigt darin nur dateien an, die MP3 sind....
    Allerdings werden hier ALLE Ordner aufgelistet. Ziel ist es, hier nur die Ordner aufzulisten, die MP§ Files haben.

    Wie gesagt, ich schreib morgen ein Konsolen Programm und lad es hoch, jetzt mus ich erstmal zum Training...

    Danke Dir bishierher... melde mich

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. try
    6. {
    7. var folders = Directory.GetDirectories(path);
    8. var files = Directory.GetFiles(path, "*.mp3");
    9. if (folders.Length > 0 )
    10. {
    11. items.AddRange(folders.Select(f => new DirectoryItem { FullPath = f, Type = DirectoryItemTypeEnum.Folder }));
    12. }
    13. if(files.Count() > 0)
    14. {
    15. items.AddRange(files.Select(f => new DirectoryItem { FullPath = f, Type = DirectoryItemTypeEnum.File }));
    16. }
    17. }
    18. catch (UnauthorizedAccessException)
    19. {
    20. Console.WriteLine("") ;
    21. }
    22. return items;
    23. }


    EDIT: Sorry, vielleicht noch die DirectoryItem Klasse:

    C#-Quellcode

    1. public class DirectoryItem : ModelBase
    2. {
    3. public string FullPath { get; set; }
    4. public string Name => Type == DirectoryItemTypeEnum.Drive ? FullPath : FullPath.GetFileFolderName();
    5. public DirectoryItemTypeEnum Type { get; set; }
    6. }
    7. public enum DirectoryItemTypeEnum
    8. {
    9. Drive,
    10. Folder,
    11. File
    12. }


    und die Extension GetFileFolderName()

    C#-Quellcode

    1. public static string GetFileFolderName(this string path)
    2. {
    3. if (string.IsNullOrEmpty(path)) return string.Empty;
    4. var replacedPath = path.Replace('/', '\\');
    5. var lastIndexOfSlash = replacedPath.LastIndexOf('\\');
    6. if (lastIndexOfSlash <= 0) return path;
    7. return path.Substring(lastIndexOfSlash + 1);
    8. }


    "Hier könnte Ihre Werbung stehen..."
    Kurzer Einwurf: Alle Directories erstmal zusammensammeln und dann mit Parallel.ForEach für jedes Directory nach MP3-Dateien schauen?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed Da bin ich über Nacht auch drauf gekommen. :thumbsup:
    @MichaHo Ersetze das erste GetFiles() durch GetDirectories(), pack die in ein Array und dann weiter mit @VaporiZed.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi,
    also, ich hab da jetzt nochmal rum probiert. Mit Parallel.ForEach funktioniert es soweit, das ich nun nur die Ordner angezeigt bekomme, die MP3 enthalten.
    2 Probleme gibt es dabei noch:
    1. es dauert genauso lange wie beim Code im Post #1
    2. ich bekomme die Files nicht IN den Ordner, sondern nur daneben...

    Hier der Code:

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. var folders = Directory.GetDirectories(path);
    6. Parallel.ForEach(folders, (folder) =>
    7. {
    8. try
    9. {
    10. var files = Directory.GetFiles(folder, "*.mp3", SearchOption.AllDirectories);
    11. if (files.Length > 0)
    12. {
    13. items.Add(new DirectoryItem { FullPath = folder, Type = DirectoryItemTypeEnum.Folder });
    14. //Parallel.ForEach(files, (file) =>
    15. //{
    16. // items.Add(new DirectoryItem { FullPath = file, Type = DirectoryItemTypeEnum.File });
    17. //});
    18. }
    19. }
    20. catch (UnauthorizedAccessException)
    21. {
    22. Console.WriteLine(""); ;
    23. }
    24. });
    25. return items;
    26. }


    Ich vermute, das das SearchOption.AllDirectories das ganze in die Länge zieht, lass ich es allerdings weg, findet er nur 1 Ordner.

    den Try/Catch brauchen wir, weil sonst ne Exception fliegt wenn er bei Ordnern ankommt, die man nicht angucken darf...
    ich glaub mein Problem beim Denken ist, das ich ja erst prüfen muss, ob der Ordner, den ich zur ItemsListe hinzufügen will MP3 Dateien enthält und wenn er das tut, dann die Files hinzufügen.
    Wie gesagt, diese Methode wird immer dann aufgerufen, wenn die Methode Expand ausgeführt wird.
    "Hier könnte Ihre Werbung stehen..."
    Naja, jetzt machst Du es, dass Du alle Directories samelst und für jeden schaust, ob mp3-Dateien in ihm oder eines seiner Unterverzeichnisse sind. Klar bremst das, da ist viel Redundanzarbeit dabei. Jeder Ordner soll nur in sich selber schauen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Liegt das daran, dass bei folders in Zeile#6 nur 1 Directory rauskommt, nämlich das TopDirectory? Da muss ja schon nach allen gesucht werden, also AllDirecories als SearchOption eingestellt werden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hmmm, nee... als Pfad kommt ja zum Beispiel C:\Users\mhoffmann\ rein, also muss er alle Ordner in diesem Pfad durchgehen und schauen ob in irgendeinem von diesen Ordnern oder Unterordnern mp3 Files vorhanden sind, wenn ja, dann füge diesen Ordner samt unterordnern (die unterordner mit mp3 files) und die mp3 files selbst in die List<DirectoryItem>.

    Als Beispiel:
    Das TreeView wird geladen und hat als Source alle Logical drives, die hole ich mit:

    C#-Quellcode

    1. public static List<DirectoryItem> GetLogicalDrives()
    2. {
    3. return Directory.GetLogicalDrives().Select(d => new DirectoryItem
    4. {
    5. FullPath = Path.GetPathRoot(d) == @"C:\" ? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) : d,
    6. Type = DirectoryItemTypeEnum.Drive
    7. }).ToList();
    8. }

    So, klickse ich nun auf den ersten Eintrag Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
    wird die Expand Methode aufgerufen, die dann über die hier besprochene Methode GetFilesFolders() die Einträge holen soll.
    Nun gibt es unterhalb des UserProfil zum Beispiel den Ordner Musik, dort dann Unterordner Hörbücher und Mp3.
    In Hörbücher sind keine MP3 drin, der ist (noch) leer, aber in Musik sind Unterordner (Pop, Kinder, Rock) und dort sind MP3 drin.

    Also müsste im Treeview dann so aussehen:
    C:\....
    Musik
    MP3
    Pop
    Dateien
    Rock
    Dateien
    Kinder
    Dateien

    Sortierung mal aussen vor.
    Des Weiteren könnte es aber noch andere Ordner geben, die MP3 enthalten, die müssten dann eben auch in der Struktur angezeigt werden.
    Das ist echt kompliziert....
    "Hier könnte Ihre Werbung stehen..."
    Mein Gedankengang: Alle Directories holen. Parallel.ForEach mit diesem DirectoryArray. Soweit besteht - glaube ich - Einigkeit/Klarheit über meinen Vorschlag. Sobald dann ein Verzeichnis meldet: Ich beinhalte mp3-Dateien (direkt, nicht über ein Subdirectory!), dann kann doch dieses Verzeichnis sich in eine List(Of IO.DirectoryInfo) oder notfalls List(Of String) eintragen. Und zwar mit komplettem Pfad. Sobald alle mp3-beinhaltenden Verzeichnisse erfasst wurden, schmeißt man mit .Distinct alle doppelten Einträge weg. Klar, die fertige Liste muss jetzt noch aufgearbeitet werden, damit das TreeView sauber aussieht. Aber vor der Aufarbeitung erstmal testen, wie lange das Ganze dauert. Wenn die Zeitersparnis natürlich bis dahin inakzeptabel minimal ist, bleib bei dem, was Du hast.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hi @VaporiZed

    VaporiZed schrieb:

    Alle Directories holen. Parallel.ForEach mit diesem DirectoryArray. Soweit besteht - glaube ich - Einigkeit/Klarheit über meinen Vorschlag.


    Da stimme ich Dir zu. Ich hab eine Teillösung, es dauert nicht sooo lange wie bei der Variante aus Post#1 aber es dauert ein wenig (ca. 10 Sekunden).

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. DirectoryInfo[] folders = new DirectoryInfo(path).GetDirectories();
    6. Parallel.ForEach(folders, (folder) =>
    7. {
    8. if (folder.HasMP3Files())
    9. items.Add(new DirectoryItem { FullPath = folder.FullName, Type = DirectoryItemTypeEnum.Folder } );
    10. });
    11. return items;
    12. }


    hab mir dazu noch ne Extension gebaut HasMP3Files um zu schauen, ob der aktuelle Ordner MP3 Files hat.
    Wobei das SearchOption.AllDirectories die Bremse zu sein scheint. Wenn ich es allerdings weg lasse wird wieder nur 1 Folder angezeigt, ebenso bei TopDirectoryOnly.

    C#-Quellcode

    1. public static bool HasMP3Files(this DirectoryInfo dirInfo)
    2. {
    3. try
    4. {
    5. return dirInfo.GetFiles("*.mp3").Length > 0;
    6. }
    7. catch (UnauthorizedAccessException)
    8. {
    9. return false;
    10. }
    11. }


    So, nun hab ich die Ordner, die MP3 enthalten.... jetzt müssen da noch die Dateien selbst rein.

    EDIT: habs geschafft

    C#-Quellcode

    1. public static List<DirectoryItem> GetFilesFolders(this string path)
    2. {
    3. var items = new List<DirectoryItem>();
    4. if (!Directory.Exists(path)) return new List<DirectoryItem>();
    5. DirectoryInfo[] folders = new DirectoryInfo(path).GetDirectories();
    6. FileInfo[] files = new DirectoryInfo(path).GetFiles("*.mp3");
    7. Parallel.ForEach(folders, (folder) =>
    8. {
    9. if (folder.HasMP3Files())
    10. items.Add(new DirectoryItem { FullPath = folder.FullName, Type = DirectoryItemTypeEnum.Folder } );
    11. });
    12. if(files.Length > 0)
    13. {
    14. items.AddRange(files.Select(f => new DirectoryItem { FullPath = f.FullName, Type = DirectoryItemTypeEnum.File }));
    15. }
    16. return items;
    17. }



    "Hier könnte Ihre Werbung stehen..."

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „MichaHo“ ()

    Wenn du per foreach enumerierst, dann benutz mal Directory.EnumerateDirectories. Das sollte schneller sein. Für Files das Gleiche. GetDirectories muss nämlich erst alle Directories auflisten. EnumerateDirectories lässt dich schon enumerieren bevor die gesamte Collection vollständig ist.