String in Verzeichnisstruktur umwandeln

  • C#
  • .NET (FX) 4.0

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von _Andy_.

    Das verstehe ich jetzt also nicht, die Liste mit den Unterordnern, die enthält dann doch eigentlich die Namen?

    Ja - insbesondere benötigt meine Ergänzung mehr Speicherplatz- was bei sehr vielen Objekten auch ein Nachteil sein kann - wobei es ja meist weniger Verzeichnisse als Dateien gibt.

    Wenn dass Programm z.B. im root-Verzeichnis ist, dann gibt es jetzt mit "DirNames" halt sofort eine Liste mit allen Directorys im root-Verzeichnis.

    Andernfalls ohne "DirNames" mus man erst mit einer Schleife durch das root-Verzeichnis gehen, um sich alle Verzeichnisse zu suchen.

    Wenn einen diese Schleife nicht stört - man kein Contains braucht - dann selbstverständlich "DirNames" weglassen!

    Selber denke ich sogar darüber nach, ein neues Indexierungs-Objekt in die Klasse einzubauen (verbraucht dann noch mehr Speicher) - so dass sofort auflgelistet werden kann, in welchem Verzeichnis sich eine Datei befindet.
    Aber meine Problemstellung ist letztendlich auch eine ganz andere - ich werde das Projekt für eine Darstellung von Produktionsabläufen anpassen - da gibt es dann Artikel, Betriebsauftragnummern, Maschinen, Stücklistenteile, Bestellungen beim Lieferanten, Produktionstermine, Kundentermine etc.

    @EDR - viele Kommentare empfinde ich aktuell auch mehr als überflüssig - aber wenn ich häufig nach über 1 Jahr wieder in alte Programme reinschaue, dann bin ich zumindest häufig über "überflüssige" Kommentare dankbar, weil die mich sofort daran erinnen was da abgeht

    Auf Grund Deiner vielen Programmierarbeit mit vb.net ist Dir vieles sofort klarer, als für jemanden der vielleicht erst 20.000 Zeilen Code mit dieser speziellen Programmiersprache geschrieben hat.

    Ja ich weiß, dass man anstatt:

    VB.NET-Quellcode

    1. Dim curSubDir As String = segments(i)
    2. bool = MeSubDir.DirNames.Contains(curSubDir)
    3. If bool = False Then
    4. Exit For
    5. End If

    in vb.net eher folgendes schreibt:

    VB.NET-Quellcode

    1. If Not MeSubDir.DirNames.Contains(segemts(i)) Then
    2. Exit For
    3. End If

    (Überflüssige) Einzelschritte helfen mir aber manchmal den Codeablauf besser zu verstehen / zu lesen - was für Dich natürlich so trivial und überflüssig und sogar hinderlich ist, so wie meine Kommentare.

    Nach 100.000 Zeilen programmierten Code in vb.net werde ich wahrscheinlich auch die kompakteren Schreibweisen bevorzugen.

    Nach 20 Jahten Programmierung in einer komplett anderen Programmiersprache (RPGLE) müssen sich erst einmal ganz langsam diverse Denkstrukturen im Gehirn umstellen!

    Edit: Vielen Dank @ErfinderDesRades für diesen tollen Programmierkurs (Post 22)! :)

    Nachtrag zu Post 23 von @_Andy_: Sehr schön strukturiert, habe es schon nach VB.Net übersetzt.
    Werde - wenn ich Zeit dazu finde - in den VB.Net-Code noch ein paar Änderungen vom @ErfinderDesRades reinbauen und dann veröffentlichen, sofern mir niemand zuvorkommt.

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Thias“ ()

    jo, also ich täte dich ermutigen, konsequenter gegen unnützen Kram vorzugehen.
    Das heisst nicht, dass nun Zwischenschritte unbedingt in eine Zeile gestopft werden müssen. Wenn Zwischenschritte Debuggen und Verständnis verbessern, dann sind sie ja nicht unnützer Kram.

    Auch gehört dazu, sehr präzise zu benamen - Nomen est Omen!, guck:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' withFiles gibt an, ob die fullNames als Datei- oder als Ordner-Pfade aufzufassen sind
    3. ''' </summary>
    4. Public Sub AddPathes(fullNames As String(), Optional withFiles As Boolean = False)
    • ich hab eine Sub davon gemacht, denn es ist unnütz, dass das Objekt sich selbst returnt.
    • Die Methode heißt AddPathes, denn sie added mehrere Pathes
    • Der Kommentar erklärt, was evtl. nicht gleich verstanden wird.
    Ich bin mir ganz sicher, das verstehst du auch noch in 10 Jahren.
    nu mal dieses angugge:

    VB.NET-Quellcode

    1. Dim bool As Boolean = False
    2. Dim i As Integer = 0
    3. Dim j As Integer = 0
    4. ' Pfad in Verzeichnis-Segmente aufteilen
    5. Dim segments As String() = Path.Split(New String() {"\"}, StringSplitOptions.RemoveEmptyEntries)
    6. Dim segLength As Integer = segments.Length - 1
    7. If withFiles = True Then
    8. segLength -= 1
    9. End If

    bool ist kein Name für garnix - klar versteht man dann nix mehr. Nenne das Ding isFullnameKnown oder so, dann weiß man , wozu es gut ist.
    j kann weg, die Variable ist in der Schleife gut deklariert, wenns soweit ist - Voraus-Deklarationen müssen einen Grund haben (wie etwa das i)
    Der Kommentar - was sagt der anderes als was die Codezeile darunter nicht selbst sagt: Path wird in Segmente gesplittet - das steht da doch schon.
    segLenth ist falsch benamt, denn eine Length gibt immer die Anzahl der Elemente an. Was du da aber berechnest ist der höchste noch zulässige Index. Sowas würde ich upperBound nennen, angelehnt an die Array-Klasse, deren .GetUpperBound-Function ein gleiches ermittelt. Oder kurz uBound, wie es noch in vb6 hieß.
    Solche falschen Benamungen führen in 10 Jahren dazu, dass du Code nicht verstehst.
    Und Path würde ich fullname nennen, passend zu wie ich ja auch das String-Array umgetauft habe. Zumal Path auch quasi ein belegtes Wort ist, es gibt ja eine (sehr wichtige) Klasse dieses Namens.
    Hiermal Komplett:

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' withFiles gibt an, ob die fullNames als Datei- oder als Ordner-Pfade aufzufassen sind
    3. ''' </summary>
    4. Public Sub AddPathes(fullNames As String(), Optional withFiles As Boolean = False)
    5. For Each fullName As String In fullNames
    6. Dim segments As String() = fullName.Split(New String() {"\"}, StringSplitOptions.RemoveEmptyEntries)
    7. Dim allSegmentsKnown As Boolean = True
    8. Dim uBound = segments.Length - If(withFiles, 2, 1) ' bei withFiles ist das letzte Segment kein Ordner
    9. Dim currDir As Directory = Me
    10. Dim i As Integer = 0
    11. For i = 0 To uBound
    12. Dim curSegment As String = segments(i)
    13. Dim idxSubDir As Integer = currDir.DirNames.IndexOf(curSegment)
    14. If idxSubDir < 0 Then
    15. allSegmentsKnown = False
    16. Exit For
    17. End If
    18. currDir = currDir.SubDirs(idxSubDir)
    19. Next
    20. If Not allSegmentsKnown Then
    21. ' Ab dem Verzeichniseintrag, welcher nicht im Directory gefunden wurde, die restlichen Verzeichniseinträge hinzufügen!
    22. For j = i To uBound
    23. Dim curSegment As String = segments(j)
    24. Dim newDir = New Directory(curSegment)
    25. currDir.AddDir(newDir)
    26. currDir = newDir
    27. Next
    28. End If
    29. ' ToDo: Hier müsste noch eine Prüfung hin, ob File schon vorhanden ist
    30. If withFiles Then currDir.AddFile(segments(uBound + 1))
    31. Next ' fullName
    32. End Sub
    Ist das wirklich schwerer zu verstehen als dein Code? Allenfalls Zeile#8 zeigt ein Sprach-Feature, was dir evtl. neu ist, dann lerns halt (wobei es sich ja hier auch selbst erklärt, schlimmstenfalls mit Blick auf den Comment).
    Jdfs in 10 Jahren wirst du das Imediate-If mit Sicherheit kennen.
    (und dank .IndexOf wird Contains() übrigens garnet benötigt.)
    So, habe den Quellcode jetzt mal etwas geändert.

    Spoiler anzeigen

    C#-Quellcode

    1. public class Directory
    2. {
    3. public string Name = string.Empty;
    4. public List<Directory> SubDirs = new List<Directory>();
    5. public List<string> Files = new List<string>();
    6. private Directory parent = null;
    7. public Directory Parent
    8. {
    9. get
    10. {
    11. return this.parent;
    12. }
    13. }
    14. public Directory(string Name)
    15. {
    16. this.Name = Name;
    17. }
    18. public string ToFullPath()
    19. {
    20. Action<Directory> searchAction = null;
    21. List<string> paths = new List<string>();
    22. searchAction = new Action<Directory>((Directory dir) =>
    23. {
    24. if (dir.parent == null)
    25. {
    26. paths.Add(dir.Name);
    27. return;
    28. }
    29. paths.Add(dir.Name);
    30. searchAction(dir.parent);
    31. });
    32. searchAction(this);
    33. paths.Reverse();
    34. return String.Join(@"BACKSLASH", paths);
    35. }
    36. public void SaveDirectories(string path)
    37. {
    38. List<string> paths = new List<string>();
    39. Action<Directory> passDirs = null;
    40. Directory node = this;
    41. passDirs = new Action<Directory>((Directory currentDir) => {
    42. foreach (Directory dir in currentDir.SubDirs)
    43. {
    44. paths.Add(dir.ToFullPath());
    45. passDirs(dir);
    46. }
    47. });
    48. passDirs(node);
    49. System.IO.File.WriteAllText(path, String.Join(Environment.NewLine, paths));
    50. }
    51. public static Directory ReadFromFile(string filePath)
    52. {
    53. Directory readNode = new Directory("");
    54. foreach (string path in System.IO.File.ReadAllText(filePath).Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
    55. {
    56. if (path.Length > 0 && path[0] == 'BACKSLASHBACKSLASH')
    57. readNode.AddPathes(new string[] { path });
    58. else
    59. {
    60. string[] segments = path.Split(new string[] { @"BACKSLASH" }, StringSplitOptions.RemoveEmptyEntries);
    61. string nPath = string.Empty;
    62. readNode.Name = segments[0];
    63. for (int i = 1; i <= segments.Length - 1; i++)
    64. nPath += segments[i] + @"BACKSLASH";
    65. readNode.AddPathes(new string[] { nPath });
    66. }
    67. }
    68. return readNode;
    69. }
    70. public void AddPathesWithFiles(string[] data)
    71. {
    72. string[] directorys = new string[data.Length];
    73. for (int i = 0; i <= data.Length - 1; i++)
    74. {
    75. string[] splittedData = data[i].Split(new string[] { @"BACKSLASH" }, StringSplitOptions.RemoveEmptyEntries);
    76. for (int s = 0; s <= splittedData.Length - 2; s++)
    77. directorys[i] += splittedData[s] + @"BACKSLASH";
    78. }
    79. this.AddPathes(directorys);
    80. this.AddFiles(data);
    81. }
    82. public bool Remove(string path)
    83. {
    84. if (!path.Contains(@"BACKSLASH"))
    85. {
    86. int index_ = this.IndexOf(path);
    87. if (index_ != -1)
    88. this.SubDirs.RemoveAt(index_);
    89. return index_ != -1;
    90. }
    91. string[] segments = path.Split(new string[] { @"BACKSLASH" }, StringSplitOptions.RemoveEmptyEntries);
    92. Directory firstNode = this;
    93. int index = 0;
    94. while (index <= segments.Length - 1)
    95. {
    96. foreach (Directory subDir in firstNode.SubDirs)
    97. {
    98. if (segments[index] == subDir.Name)
    99. {
    100. firstNode = subDir;
    101. index++;
    102. break;
    103. }
    104. }
    105. }
    106. if (firstNode != null && firstNode.Parent != null)
    107. firstNode.Parent.SubDirs.Remove(firstNode);
    108. else
    109. return false;
    110. return true;
    111. }
    112. public bool Contains(string dir)
    113. {
    114. foreach (Directory currentDir in this.SubDirs)
    115. if (currentDir.Name == dir)
    116. return true;
    117. return false;
    118. }
    119. public int IndexOf(string dir)
    120. {
    121. for (int i = 0; i <= this.SubDirs.Count() - 1; i++)
    122. if (this.SubDirs[i].Name == dir)
    123. return i;
    124. return -1;
    125. }
    126. public void AddFile(string fileName)
    127. {
    128. this.AddFiles(new string[] { fileName });
    129. }
    130. public void AddFiles(string[] fileName)
    131. {
    132. foreach (string currentFileName in fileName)
    133. {
    134. if (!currentFileName.Contains(@"BACKSLASH"))
    135. {
    136. this.Files.Add(currentFileName);
    137. continue;
    138. }
    139. Directory currentNode = this;
    140. string[] segments = currentFileName.Split(new string[] { @"BACKSLASH" }, StringSplitOptions.RemoveEmptyEntries);
    141. for (int i = 0; i <= segments.Length - 2; i++)
    142. {
    143. if (currentNode.Contains(segments[i]))
    144. currentNode = currentNode.SubDirs[currentNode.IndexOf(segments[i])];
    145. else
    146. break;
    147. }
    148. if (segments.Length > 0 && !currentNode.Files.Contains(segments[segments.Length - 1]))
    149. currentNode.Files.Add(segments[segments.Length - 1]);
    150. }
    151. }
    152. public void AddPathes(string[] path)
    153. {
    154. foreach (string dirName in path)
    155. {
    156. string[] segments = dirName.Split(new string[] { @"BACKSLASH" }, StringSplitOptions.RemoveEmptyEntries);
    157. Directory currentNode = this;
    158. bool goOn = false;
    159. int i = 0;
    160. if (currentNode.SubDirs.Count() > 0)
    161. {
    162. for (; i <= segments.Length - 1; i++)
    163. {
    164. string currentName = segments[i];
    165. goOn = currentNode.Contains(currentName);
    166. if (!goOn)
    167. break;
    168. int indexSubDir = currentNode.IndexOf(currentName);
    169. currentNode = currentNode.SubDirs[indexSubDir];
    170. }
    171. }
    172. if (!goOn)
    173. {
    174. for (int j = i; j <= segments.Length - 1; j++)
    175. {
    176. string currentName = segments[j];
    177. Directory nd = new Directory(currentName);
    178. nd.parent = currentNode;
    179. currentNode.SubDirs.Add(nd);
    180. int indexSubDir = currentNode.IndexOf(currentName);
    181. currentNode = currentNode.SubDirs[indexSubDir];
    182. }
    183. }
    184. }
    185. }
    186. }


    Der Aufruf wäre also der folgende:

    C#-Quellcode

    1. Directory dir = new Directory("root");
    2. string[] data = new string[] {
    3. @"\Test\Test1\test.xml",
    4. @"\Test\Test2\test1.xml",
    5. @"\Test1\xy.txt",
    6. };
    7. dir.AddPathesWithFiles(data);


    Ich habe jetzt die Liste von @Thias weggelassen und meine Contains-Methode dient sozusagen als Ersatz, genauso wie IndexOf.
    Die Contains-Methode von @Thias werde ich gleich auch noch einbinden, da diese sehr praktisch sein kann.
    Nur jetzt ist bei @'Thias''-Ansatz beim Hinzufügen eines Pfades jetzt wieder mit den Dateien drin. Ich habe das jetzt einfach
    mal getrennt, weil ich das vermeiden möchte.
    Achja: @'Thias', du hattest eine Methode AddFile, die einfach nur die Datei zur Liste hinzufügt - also ein Befehl ausführt. Wenn man
    das später erweitert ist das auf jeden Fall sinnvoll (eigentlich könnte man die Methode weglassen). Aber ich hatte - wie schon gesagt - die Files getrennt behandelt, also sieht das bei mir
    noch etwas anders aus.

    Edit: Habe jetzt noch eine Methode eingefügt ToFullPath(), damit man eben den gesamten Pfad eines Ordners ermitteln kann, ich glaube @Thias hatte das schon irgendwo erwähnt.
    Dazu wird beim Hinzufügen der Pfade dem Unterordner immer ein Eltern-Ordner zugewiesen.
    Der Code dafür:

    C#-Quellcode

    1. string[] data = new string[]
    2. {
    3. @"\Test\Test1\Test1",
    4. @"\Test\Test1\Test2",
    5. @"\Test\Test1\Test3",
    6. @"\Test\Test2\Test1",
    7. @"\Test\Test2\Test2",
    8. @"\Test\Test2\Test3"
    9. };
    10. Directory dir = new Directory("root");
    11. dir.AddPathes(data);
    12. Action<Directory> passDirs = null;
    13. passDirs = delegate (Directory d)
    14. {
    15. foreach (Directory currentDir in d.SubDirs)
    16. {
    17. Console.Write(currentDir.ToFullPath() + Environment.NewLine);
    18. passDirs(currentDir);
    19. }
    20. };
    21. passDirs(dir);


    Als Ausgabe erhalte ich dann:

    root\Test
    root\Test\Test1
    root\Test\Test1\Test1
    root\Test\Test1\Test2
    root\Test\Test1\Test3
    root\Test\Test2
    root\Test\Test2\Test1
    root\Test\Test2\Test2
    root\Test\Test2\Test3

    @RodFromGermany

    C#-Quellcode

    1. DirectoryInfo mainDir = new DirectoryInfo(@"C:\temp\XTest");
    2. string[] data = new string[]
    3. {
    4. @"Test\Test1\Test1\",
    5. @"Test\Test1\Test2\",
    6. @"Test\Test1\Test3\",
    7. @"Test\Test2\Test1\"
    8. };
    9. foreach (string path in data)
    10. {
    11. // ROD:
    12. mainDir.CreateSubdirectory(path);
    13. // Andy:
    14. System.IO.Directory.CreateDirectory(System.IO.Path.Combine(mainDir.FullName, path));
    15. }


    Das Ergebnis ist gleich, also so meinte ich das. Hätte ich die Pfade natürlich nicht zusammenkombiniert, dann klar, dann hätte ich ein Problem.

    Edit 2:
    Sorry, für die vielen Edits. Aber ich habe jetzt noch die Methode Remove hinzufügt und wenn eine Datei-Pfad keinen Backslash enthält, so wird dies als
    Datei angenommen und direkt zum Ordner hinzugefügt.

    Edit 3:
    @Thias: Ich hatte deinen 2ten Edit gar nicht mehr gesehen. Die Benamung von mir ist auch nicht ganz so optimal, allerdings weiß ich, was wofür steht - und
    das auch noch später. Ich bin gespannt auf deine Übersetzung - hast du auch schon die neuen Methoden eingebaut?

    Das mit der Serialisierung hat mir nicht gefallen - wir haben ja extra Methoden um Pfade problemlos einlesen zu können.
    Ich habe also jetzt die Methoden ReadFromFile und Save eingefügt, dort sind aber noch nicht die Files miteinbezogen!

    Dieser Beitrag wurde bereits 14 mal editiert, zuletzt von „_Andy_“ ()