Angepinnt [Sammelthread] Knobel-Aufgaben, knifflige Algorithmen, elegante Lösungen

    • VB.NET

    Es gibt 178 Antworten in diesem Thema. Der letzte Beitrag () ist von Thunderbolt.

      Sonst mal so:

      VB.NET-Quellcode

      1. Public Function AppendNumber2(sourceFile As FileInfo, destFolder As DirectoryInfo) As FileInfo
      2. Dim item = destFolder.GetFiles("*" & Path.GetFileNameWithoutExtension(sourceFile.Name) & "*").Reverse.ToList.FirstOrDefault()
      3. Dim filename = Path.GetFileNameWithoutExtension(sourceFile.Name)
      4. Dim s2 As String = "00"
      5. If Not item Is Nothing Then
      6. Dim s = Path.GetFileNameWithoutExtension(item.Name).Replace(filename & "_", "")
      7. s2 = (Integer.Parse(s) + 1).ToString("D2")
      8. End If
      9. Dim newBackupFile As New FileInfo(Path.Combine(destFolder.FullName, filename & "_" & s2 & ".zip"))
      10. sourceFile.CopyTo(newBackupFile.FullName, True)
      11. Return newBackupFile
      12. End Function


      Quasi leicht abgeändert.

      Find diese Variante verständlicher als eine LINQ Verschachtelung hoch 10.

      lg
      ScheduleLib 0.0.1.0
      Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
      Mal eine grundsätzliche Frage an den TE @ErfinderDesRades:

      Ist dieser Thread ausschließlich für echte Probleme?
      ... oder auch für selber ausgedachte Rätsel?
      ... deren Lösung ich weiß oder nicht weiß?
      ... ausschließlich für VB.NET oder auch andere Sprachen?

      Die bisherigen "Fragen" waren ja nicht so knobel mäßig und auch nicht besonders schwer.
      jo, ich denke schon seit einiger Zeit über die genaue Definition von "Knobelei" nach.
      Vlt. ist "Knobelei" auch nicht das perfekt korrekte Wort - es geht eher darum, klar defnierte Problemstellungen möglichst elegant zu lösen. Dabei gibts natürlich unendlich viele Lösungen, während eine klassische Knobelei nur eine definierte Menge von Lösungen hat.

      Zu einer normalen Frage gehört ja üblicherweise eine oft ausgedehnte Phase des Ringens um eine eindeutige Formulierung der Problemstellung, und der Suche nach der geeigneten Technologie.
      Vorher kann man ja garnet anfangen, etwas auszuprogrammieren.
      Bei Knobeleien, wie ichs hier auffasse, ist diese Phase bereits bewältigt, und es geht "nur noch" um Ausprogrammierung - deshalb auch im Source-Code-Austausch.

      Ich habs auch mal so formuliert gehabt:
      Abgrenzung von "normalen" Forum-Fragen (tendenziell):
      • keine Konzept-Diskussionen (die sollten durch den Startpost bereits erledigt sein)
      • Fertig-Lösungen ausdrücklich erwünscht
      • keine Hilfe zur Selbsthilfe - nicht anregen, vorschlagen, verweisen - sondern selber machen
      Der letzte Punkt ist glaub das Entscheidende, was Knobeleien so grundsätzlich von Frage-Threads unterscheidet: Nämlich genau das ist hier gewünscht, was in Frage-Threads den Fragestellern immer eher schadet als nützt, weils die Selbst-Hilfe-Kräfte lähmt.

      Anforderung an einen Knobelei-Start-Post (tendenziell):
      • klare, eingegrenzte und auskonzipierte Aufgabenstellung
      • geringer Umfang (also nicht: "schreibt einen Mathe-Parser")
      • Input/Output Definition
      • Input/Output Beispiel (wünschenswert)
      • Angabe der Signatur einer Methode, die einen Input zu einem Output verarbeiten würde (wünschenswert)
      • meist ist das Anhängen einer Test-Anwendung sehr sinnvoll, a) weil jeder Knobler eh eine braucht, b) damit in einem einheitlichem Rahmen entwickelt wird, c) damit Lösungen auch gegengeprüft werden können.


      Ob die Probleme echt sind oder nicht, ist ja egal, und auch, ob du "die" Lösung weißt (da kann man immer noch was eleganteres ausknobeln ;))
      Natürlich sind reale Probleme reizvoller zu lösen, als iwas hingesponnenes.
      Mir wärs am liebsten, wir könnten jeweils bei einer Sprache bleiben. Weil zum Testen muss man doch fast immer eine Extra Anwendung aufsetzen, und das ist bei 2-sprachigkeit doppelte Arbeit. Oder man muss ständig mittm CodeConverter rumfuchteln, und die Dinger sind halt nie 100% zuverlässig.

      Dass du sagst, es sei bisher nicht so schwer, wundert mich - grad hier bei der letzten Frage ist noch keine wirklich wasserdicht funktionierende Lösung eingegangen, soweit ich sehe.
      Also eine, die es zB. auch ertrüge, wenn ein Witzbold als DateiEndung "._temp" angibt, oder eine Source-Datei ohne Endung.

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

      Soll bei dem Backup Dinges nun das letzte Backup die höchste Nummer haben (Beispiel: xxx_08.zip ist das letzte -> neues Backup xxx_09.zip) oder neues Backup xxx_00.zip (und alle anderen um 1 erhöhen)?

      lg
      ScheduleLib 0.0.1.0
      Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten

      sonne75 schrieb:

      Stand das so in der Aufgabenstellung?
      eiglich schon. Oder sollte zumindest.
      Steht da nicht drin, dass es für beliebige Extensions gehen soll?

      neues Backup xxx_00.zip (und alle anderen um 1 erhöhen)?
      kann man auch machen.
      Wie gesagt: Sinngemäß gehts darum, dass man an den Nummern ablesen kann, in welcher Reihenfolge die Backupse erstellt wurden.
      Unds muss auch im Explorer nach dem Namen sortierbar sein - aber das ist ja mit Numb.ToString("00") ein Klacks.

      VB.NET-Quellcode

      1. Public Class frmFileSaver
      2. Private _srcFile As New FileInfo("..\..\Test")
      3. Private _destFolder As New DirectoryInfo("..\..\Backups")
      4. Private Sub btSaveFile_Click(sender As System.Object, e As System.EventArgs) Handles btSaveFile.Click
      5. _srcFile.CopyTo(AppendNumber(_srcFile, _destFolder))
      6. End Sub
      7. Public Shared Function AppendNumber(ByVal sourceFile As FileInfo, ByVal destFolder As DirectoryInfo) As String
      8. Dim newNmb = 0
      9. Dim lst As New List(Of Integer)
      10. Dim NewName As String
      11. Dim filename = sourceFile.Name
      12. Dim ind = sourceFile.Name.IndexOf("."c)
      13. If ind > 0 Then filename = sourceFile.Name.Substring(0, ind)
      14. Dim backfilesNmb = From f In destFolder.GetFiles Where f.Name.StartsWith(filename) Select f.Name.Split("_"c)(1)
      15. If backfilesNmb.Count > 0 Then
      16. For Each nmb In backfilesNmb
      17. Dim n = 0
      18. Integer.TryParse(nmb.Substring(0, nmb.IndexOf(".")), n)
      19. lst.Add(n)
      20. Next
      21. newNmb = lst.Max() + 1
      22. End If
      23. NewName = filename & "_" & newNmb.ToString("D2") & ".zip"
      24. Return Path.Combine(destFolder.FullName, NewName)
      25. End Function
      26. End Class


      Das müsste mit oder ohne Endung vom Ursprungsfile funktionieren (habe ohne Endung getestet).
      Variante 1:
      Neues Backup hat immer _00.zip und der Rest wird um 1 erhöht.

      VB.NET-Quellcode

      1. Public Function AppendNumber(sourceFile As FileInfo, destFolder As DirectoryInfo) As FileInfo
      2. Dim lst = destFolder.GetFiles("*" & Path.GetFileNameWithoutExtension(sourceFile.Name) & "*").Reverse.ToList
      3. Dim filename = Path.GetFileNameWithoutExtension(sourceFile.Name)
      4. For Each item In lst
      5. Dim s = item.Name.Replace(filename & "_", "")
      6. Dim s2 = (Integer.Parse(Path.GetFileNameWithoutExtension(s)) + 1).ToString("D2")
      7. Dim newfile = Path.Combine(destFolder.FullName, filename & "_" & s2 & ".zip")
      8. item.MoveTo(newfile)
      9. Next
      10. Dim newBackupFile As New FileInfo(Path.Combine(destFolder.FullName, filename & "_00.zip"))
      11. sourceFile.CopyTo(newBackupFile.FullName, True)
      12. Return newBackupFile
      13. End Function


      Variante 2:
      Neues Backup bekommt immer die höchste Nummer:

      VB.NET-Quellcode

      1. Public Function AppendNumber(sourceFile As FileInfo, destFolder As DirectoryInfo) As FileInfo
      2. Dim item = destFolder.GetFiles("*" & Path.GetFileNameWithoutExtension(sourceFile.Name) & "*").Reverse.ToList.FirstOrDefault()
      3. Dim filename = Path.GetFileNameWithoutExtension(sourceFile.Name)
      4. Dim s2 As String = "00"
      5. If Not item Is Nothing Then
      6. Dim s = Path.GetFileNameWithoutExtension(item.Name).Replace(filename & "_", "")
      7. s2 = (Integer.Parse(s) + 1).ToString("D2")
      8. End If
      9. Dim newBackupFile As New FileInfo(Path.Combine(destFolder.FullName, filename & "_" & s2 & ".zip"))
      10. sourceFile.CopyTo(newBackupFile.FullName, True)
      11. Return newBackupFile
      12. End Function


      Beide getestet und funzen eigentlich.

      lg
      ScheduleLib 0.0.1.0
      Kleine Lib zum Anlaufen von Code zu bestimmten Zeiten
      failt bei Extensions wie "._cfg" - aber ich gebe zu, das ist bisserl kranke anforderung.
      Andererseits, produktiv angewendet, muss das abgedeckt sein.

      Was aber auch failt ist

      VB.NET-Quellcode

      1. Dim ind = sourceFile.Name.IndexOf("."c)
      , namlich bei "myFile.Sonne.zip". müsste man mit LastIndexOf arbeiten.
      jo, nützt aber nix.
      Für wenn du sowas in einr Dll gecodet haben wolltest, muss das wasserdicht.
      Ich hab jetzt mal den Extra-Fies-Tester gecodet:

      VB.NET-Quellcode

      1. Private _testFiles As String() = "Test.txt;Te st.txt;Te_st._cfg;Test.Test.txt".Split(";"c)
      2. Private _destFolder As New DirectoryInfo("..\..\Backups")
      3. Private Sub btSaveFile_Click(sender As System.Object, e As System.EventArgs) Handles btSaveFile.Click
      4. For Each fi In _testFiles.Select(Function(s) New FileInfo("..\.." & s))
      5. fi.CopyTo(AppendNumber(fi, _destFolder))
      6. Next
      7. End Sub
      8. Public Shared Function AppendNumber(sourceFile As FileInfo, destFolder As DirectoryInfo) As String
      9. Return Path.Combine(destFolder.FullName, "HierJetztDerNeueDateiName")
      10. End Function
      also auch für mit Spaces und '_' und sowas.

      (Die Test-Dateien sind mit drin)
      Dateien
      • FileSaver03.zip

        (13,47 kB, 146 mal heruntergeladen, zuletzt: )

      VB.NET-Quellcode

      1. Public Shared Function AppendNumber(ByVal sourceFile As FileInfo, ByVal destFolder As DirectoryInfo) As String
      2. Dim newNmb = 0
      3. Dim lst As New List(Of Integer)
      4. Dim NewName As String
      5. Dim filename = sourceFile.Name
      6. Dim ind = sourceFile.Name.LastIndexOf("."c)
      7. If ind > 0 Then filename = sourceFile.Name.Substring(0, ind)
      8. ' filename = Path.GetFileNameWithoutExtension(sourceFile.Name)
      9. Dim backfilesNmb = From f In destFolder.GetFiles Where f.Name.StartsWith(filename) Select f.Name.Split("_"c)
      10. If backfilesNmb.Count > 0 Then
      11. For Each nmbArr In backfilesNmb
      12. Dim n = 0
      13. Dim nmb = nmbArr(nmbArr.Length - 1)
      14. Integer.TryParse(nmb.Substring(0, nmb.IndexOf(".")), n)
      15. lst.Add(n)
      16. Next
      17. newNmb = lst.Max() + 1
      18. End If
      19. NewName = filename & "_" & newNmb.ToString("D2") & ".zip"
      20. Return Path.Combine(destFolder.FullName, NewName)
      21. End Function
      Schon mal aufgefallen, dass das eigentlich nicht so sinnvoll ist? Wenn dus in der Reihenfolge des Erstellens haben willst, dann sortiers einfach nach dem Erstelldatum. Um trotzdem eindeutige Dateinamen zu erzeugen fügt man das Erstelldatum auch dort ein. Wenns unbedingt nach dem namen sein soll, es gibt ja auch Datumsschreibweisen, die korrekte String-Sortierung ermöglichen, aber Datum wäre hier auf jeden Fall der korrekte Weg, keine Indizes.
      also für mein Backup-Tool reichen Indizees vollkommen.

      Aber ist echt eine gute Idee: Wenn man schlicht den aktuellen Timestamp einfrickelt, in alphabetisch sortierbarer Formatierung, dann spart man sich die ganze Wühlerei im Dateisystem!

      Artentus, du bist ein Spielverderber! ;)