VB Verarbeitung Textdatei in Listview

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Spike.

    VB Verarbeitung Textdatei in Listview

    Hallo zusammen,

    ich habe eine Frage zu dem befüllen einer ListView von einer Textdatei / Log aus.
    Das schreiben von einträgen in die Textdatei funktioniert hierbei super und ohne Probleme, auch das Splitten der Einträge innerhalb der Log und erneutes ausgeben in einem String funktioniert ohne weiteren Problem.
    Den String übergebe ich dann derzeit an das ListView, dieser schreibt mir alle Einträge der Textdatei in die ListView und sortiert diese, die neusten Einträge stehen hierbei oben, soweit auch alles in Ordnung.

    Nun mein Problem, ich suche seit Wochen eine Möglichkeit mit dieser ich entweder die neusten Einträge einer Textdatei (da ja nach Datum eingetragen wird) in die ListView geschrieben bekomme.
    Oder die Möglichkeit die letzten 20 - 30 Zeilen in die ListView einzutragen. Ich weis das ich mit ".Length - 1" den letzten Eintrag erhalte, hier müsste ich dann theoretisch hochzählen (30 Zeilen) und diese dann in einer For Each wieder ausgeben bzw. der ListView hinzufügen. Da hängt es allerdings bei mir selbst.

    Hier mein derzeitiger Code in der Grundfunktion, ich habe hier bereits einiges hin und her Versucht, bin aber auf keine saubere Lösung gekommen.

    VB.NET-Quellcode

    1. ListView_Server_Log.View = View.Details
    2. ListView_Server_Log.BackColor = Color.Empty
    3. ListView_Server_Log.FullRowSelect = True
    4. For Each line As String In File.ReadLines("C:/Test/Logs/" + test + ".txt")
    5. ' Display the line.
    6. Dim stringArray = line.Split({" "c}, StringSplitOptions.None)
    7. Dim LItem As ListViewItem = ListView_Server_Log.Items.Add(stringArray(0).ToString)
    8. LItem.SubItems.Add(stringArray(1).ToString)
    9. Next
    10. ListView_Server_Log.Columns(0).Width = 150
    11. ListView_Server_Log.Columns(1).Width = 200
    12. ListView_Server_Log.Sorting = SortOrder.Descending


    Die Einträge in der Textdatei:

    VB.NET-Quellcode

    1. [09:27:19.219] Information Das steht nun drinne
    2. [09:27:20.223] Information Das steht nun drinne
    3. [09:27:21.224] Information Das steht nun drinne
    4. [09:27:22.220] Information Das steht nun drinne
    5. [09:27:23.220] Information Das steht nun drinne
    6. [09:27:24.229] Information Das steht nun drinne
    7. [09:27:25.219] Information Das steht nun drinne
    8. [09:27:26.220] Information Das steht nun drinne
    9. [09:27:27.223] Information Das steht nun drinne
    10. [09:27:28.219] Information Das steht nun drinne
    11. [09:27:29.220] Information Das steht nun drinne
    12. [09:27:30.227] Information Das steht nun drinne


    Ich würde mich sehr über Ideen oder auch Lösungsvorschläge freuen.

    Vielen Dank

    Gruß Spike
    Hallo und willkommen im Forum :)

    Ich hab mal schnell des Program bei mir nachgebaut und eine Lösung entwickelt, die ich dir jetzt vorstelle.

    Also ich baue mir da erstmal gerne eine Hilfklasse - sind ein paar Zeilen Code aber machen alles ein wenig einfacher:

    VB.NET-Quellcode

    1. Public Class LogItem
    2. Public LogDate As string
    3. Public LogText As String
    4. Public Sub New(ByVal logDate As String, ByVal logText As String)
    5. Me.LogDate = logDate
    6. Me.LogText = logText
    7. End Sub
    8. End Class


    In der Klasse speichere ich einfach einen Log Eintrag....

    Dann habe ich eine List (Of LogItem) definiert, in die ich ALLE Log Einträge reinlade:

    VB.NET-Quellcode

    1. Public LogItems As List(Of LogItem) = New List(Of LogItem)


    Dann habe ich eine Sub definert, die sich um das Einlesen von den LogEinträgen kümmert:

    VB.NET-Quellcode

    1. Public Sub ReadLogItems(fileName As String)
    2. For Each line As String In File.ReadLines(fileName)
    3. Dim stringArray = line.Split({" "c}, StringSplitOptions.None)
    4. LogItems.Add(New LogItem(stringArray(0), stringArray(1)))
    5. Next
    6. End Sub



    Und zum schluss eine Sub, die eine beliebige Anzahl von diesen LogItems in die Listbox kopieren kann:

    VB.NET-Quellcode

    1. Public Sub ShowNewestItems(count As Integer)
    2. ListView_Server_Log.Items.Clear()
    3. If count = 0 Then count = LogItems.Count - 1
    4. For i As Integer = LogItems.Count - 1 To LogItems.Count - (count) Step -1
    5. ListView_Server_Log.Items.Add(New ListViewItem(LogItems(i).LogDate)).SubItems.Add(LogItems(i).LogText)
    6. Next
    7. End Sub

    (wie du siehst, wird bei einer Übergabe von 0 die gesamten Einträge angezeigt)


    Jetzt setze ich noch beim Form_Load Ereignis die Einstellungen der Listbox, lade die Logdateien und zeige einmal alle an:

    VB.NET-Quellcode

    1. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. ListView_Server_Log.View = View.Details
    3. ListView_Server_Log.BackColor = Color.Empty
    4. ListView_Server_Log.FullRowSelect = True
    5. ListView_Server_Log.Columns(0).Width = 150
    6. ListView_Server_Log.Columns(1).Width = 200
    7. ListView_Server_Log.Sorting = SortOrder.Descending
    8. ReadLogItems("test.txt")
    9. ShowNewestItems(0)
    10. End Sub



    Und wenn ich auf einen Button drücke, werden nur noch so viele LogItems angezeigt, wie im NumericUpDown Element (nud_count) ausgewählt wurden:

    VB.NET-Quellcode

    1. Private Sub btn_show_Click(sender As Object, e As EventArgs) Handles btn_show.Click
    2. ShowNewestItems(nud_count.Value)
    3. End Sub



    Klang jetzt vielleicht komplizierter als es eigentlich war, ich denke es gibt auch "leichtere" Lösungen. So würde ich es aber machen und so ist es auch noch recht übersichtlich.
    Hier nocheinmal der gesamte Code:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public LogItems As List(Of LogItem) = New List(Of LogItem)
    3. Private Sub btn_show_Click(sender As Object, e As EventArgs) Handles btn_show.Click
    4. ShowNewestItems(nud_count.Value)
    5. End Sub
    6. Public Sub ReadLogItems(fileName As String)
    7. For Each line As String In File.ReadLines(fileName)
    8. Dim stringArray = line.Split({" "c}, StringSplitOptions.None)
    9. LogItems.Add(New LogItem(stringArray(0), stringArray(1)))
    10. Next
    11. End Sub
    12. Public Sub ShowNewestItems(count As Integer)
    13. ListView_Server_Log.Items.Clear()
    14. If count = 0 Then count = LogItems.Count - 1
    15. For i As Integer = LogItems.Count - 1 To LogItems.Count - (count) Step -1
    16. ListView_Server_Log.Items.Add(New ListViewItem(LogItems(i).LogDate)).SubItems.Add(LogItems(i).LogText)
    17. Next
    18. End Sub
    19. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    20. ListView_Server_Log.View = View.Details
    21. ListView_Server_Log.BackColor = Color.Empty
    22. ListView_Server_Log.FullRowSelect = True
    23. ListView_Server_Log.Columns(0).Width = 150
    24. ListView_Server_Log.Columns(1).Width = 200
    25. ListView_Server_Log.Sorting = SortOrder.Descending
    26. ReadLogItems("test.txt")
    27. ShowNewestItems(0)
    28. End Sub
    29. End Class
    30. Public Class LogItem
    31. Public LogDate As string
    32. Public LogText As String
    33. Public Sub New(ByVal logDate As String, ByVal logText As String)
    34. Me.LogDate = logDate
    35. Me.LogText = logText
    36. End Sub
    37. End Class




    Wenn du noch Fragen zu einzelnen Sachen hast oder was net ganz verstehst kannste gerne nachfragen :thumbsup:

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Ich empfehle dir dringend:
    Denk dir ein tragfähiges Logging-Konzept aus.
    Log-Einträge ohne Datum machen schon am nächsten Tag keine Freude mehr.
    Überleg dir ein klein Datenmodell für Log-Einträge, also dass vom Logger ein bestimmtes csv-Format zwingend verlangt wird:
    Etwa die spalten
    Zeitpunkt (als sortierbares Datum), LogTyp (error, Info, warning), Text

    Bau dir einen richtigen Log-Viewer dafür, also wo die csv in Log-Datensätze eingelesen wird, und die Datensätze werden per Databinding in einem DatagridView angezeigt.

    Dann kannst du die Log-Einträge sortieren und Filtern, und mw. auch nur die letzten 20 anzeigen - sowas ist jdfs. beim Auswerten von Logs gold wert.
    Hallo Florian,

    vielen Dank schon einmal für die tolle Aufnahme hier in dem Forum, ist man leider nicht überall so gewohnt.
    wirklich Spitze "Hut-Ab", funktioniert soweit super, ich danke dir dort schon einmal vielmals. An einer solchen Lösung (wenn auch längerer Code) hätte ich glaube noch ein paar Tage gesessen.
    Gerne komme ich noch einmal auf dein Angebot zurück.

    Eine Frage oder ein Verständnisproblem im Code oder auch in meinem vorherigen habe ich glaube ich nicht berücksichtig.
    Zum einen wollte ich eben noch aus den zwei stringArray, drei machen. Leider aber habe ich glaube ich bei dem gekürzten Code ein Verständnisproblem

    So wollte ich es machen, was aber nicht klappt:

    VB.NET-Quellcode

    1. Public Sub ShowNewestItems(count As Integer)
    2. ListView_Server_Log.Items.Clear()
    3. If count = 0 Then count = LogItems.Count - 1
    4. For i As Integer = LogItems.Count - 1 To LogItems.Count - (count) Step -1
    5. ListView_Server_Log.Items.Add(New ListViewItem(LogItems(i).LogDate)).SubItems.Add(LogItems(i).LogStatus).SubItems.Add(LogItems(i).LogText)
    6. Next
    7. End Sub

    Hier bekomme ich die Fehlermeldung "Subitems" ist kein Member von "ListViewItem.ListViewSubItem".
    Irgendwo logisch, ich hatte es auch versucht mit ".SubItems.Add(LogItems(i).LogText)" drunter zu schreiben, auch ohne Erfolg :|

    Desweiteren hatte ich nicht bedacht wie die Einträge geschrieben werden in der log Datei.
    Hier hätte ich noch folgendes:

    Durch Split wird der erste Eintrag "[09:27:19.219]" auslgesen und der zweite "Information". Nun habe ich ein weiteres Leerzeichen und dann den Text der Information.
    Der Informationstext beinhaltet natürlich auch Leerzeichen, woraufhin ich dann natürlich nur "Wort-schnippsel" erhalte. Gibt es eine Berücksichtigung das er bei der dritten Array die Leerzeichen ignoriert und den letzten Rest einfach in einer weitere String einträgt?

    Kurz zum Hintergrund: Ich hatte nicht bedacht das ich einmal in der Log die Uhrzeit -> Date habe sowie den Status -> LogState und dann natürlich den Text -> LogText
    Diese drei Dinge möcht ich natürlich in einem "Live Trace" oder dem Log als ListView mit ausgeben bzw. die eine spalte mehr.

    Vielen Dank

    Gruß
    Spike
    Hallo,

    erstmal vielen Dank für das Lob :)

    Ich versuche mal deine Fragen zu beantworten:

    1:

    Spike schrieb:

    Eine Frage oder ein Verständnisproblem im Code oder auch in meinem vorherigen habe ich glaube ich nicht berücksichtig.
    Zum einen wollte ich eben noch aus den zwei stringArray, drei machen. Leider aber habe ich glaube ich bei dem gekürzten Code ein Verständnisproblem


    Wenn du zweimal auf das Property ​SubItem "zugreifen" willst, also die Add Methode aufrufen willst, geht das nicht so, wie ich das gekürtz habe.
    Da musst du dann - wie du es auch am Anfang gemacht hast - ein Objekt mit Namen inistalisieren und dann 2 mal die SubItems.Add Methode aufrufen (denk dran, dass du dann auch 3 Spalten in deinem ListView benötigst):

    VB.NET-Quellcode

    1. For i As Integer = LogItems.Count - 1 To LogItems.Count - (count) Step -1
    2. Dim item As ListViewItem = New ListViewItem(LogItems(i).LogDate)
    3. item.SubItems.Add(LogItems(i).LogStatus)
    4. item.SubItems.Add(LogItems(i).LogText)
    5. ListView_Server_Log.Items.Add(item)
    6. Next


    Ich hoffe das ist verständlich?


    2:

    Spike schrieb:

    Durch Split wird der erste Eintrag "[09:27:19.219]" auslgesen und der zweite "Information". Nun habe ich ein weiteres Leerzeichen und dann den Text der Information.
    Der Informationstext beinhaltet natürlich auch Leerzeichen, woraufhin ich dann natürlich nur "Wort-schnippsel" erhalte. Gibt es eine Berücksichtigung das er bei der dritten Array die Leerzeichen ignoriert und den letzten Rest einfach in einer weitere String einträgt?



    Das ist das Problem mit dem Leerzeichen als Trennzeichen. Wie man es beim 3. Element ignorieren kann weiß ich nicht. Da müsstest du dann wieder "basteln". Hab ich mal kurz gemacht.

    VB.NET-Quellcode

    1. Private Function GetStringsTogether(startpoint As Integer, array As String()) As String
    2. Dim resultsting As String = ""
    3. For i As Integer = startpoint To array.Count - 1 Step 1
    4. resultsting += array(i) + " "
    5. Next
    6. Return resultsting
    7. End Function


    Was habe ich mir dabei gedacht?
    Übergeben wird eine Startposition: Wenn wir schon aus dem Array die ersten 2 "Stellen" verwendet haben - bsp für das Datum und den Status - übergeben wir hier 2 (weil 0 und 1 halt schon verbraucht sind)
    Außerdem brauchen wir das Array.

    Die Funktion hat eigentlich jetzt nur eine "For" Schleife, die ab der Stelle 2 das gesamte Array hochzählt und die Inhalte in einen String zusammensetzt und den String am Ende komplett zurückgibt.

    Unser Funktionsaufruf könnte deshalb jetzt so aussehen:

    VB.NET-Quellcode

    1. Public Sub ReadLogItems(fileName As String)
    2. For Each line As String In File.ReadLines(fileName)
    3. Dim stringArray = line.Split({" "c}, StringSplitOptions.None)
    4. LogItems.Add(New LogItem(stringArray(0), stringArray(1), GetStringsTogether(2, stringArray)))
    5. Next
    6. End Sub



    Meine Frage wäre jetzt?
    Schreibst du die Logs selber oder möchstest du sie nur auswerten.
    Wenn du sie selber schreiben würdest, schreib sie doch einfach in einem besseren Format, d.h. Datum serialisieren, damit man es auch richtig einlesen kann, statt Leerzeichen Semikolons als Trenner verwenden etc....

    Ich hoffe du hast wieder ein bisschen was verstanden und was mitnehmen können - du darfst gerne eine Rückfrage stellen :thumbsup:

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Oder die Möglichkeit die letzten 20 - 30 Zeilen


    um die letzten zeilen zu ermitteln kannst du eine Extension schreiben

    das hier in ein module

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Module Module1
    3. <Extension()>
    4. Public Function GetLast(Of T)(source As ICollection(Of T), count As Integer) As IEnumerable(Of T)
    5. Return source.Skip(Math.Max(source.Count - count, 0))
    6. End Function
    7. End Module


    ich habe hier ein Bsp. mit Datagridview

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    3. With DataGridView1
    4. .Columns.Add("ID", "ID")
    5. .Columns.Add("Feld1", "Feld1")
    6. .Columns.Add("Feld2", "Feld2")
    7. 'weitere
    8. End With
    9. End Sub
    10. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    11. 'hier gebe ich die Zeilen an die ich haben möchte = .GetLast(5).ToArray()
    12. For Each line As String In System.IO.File.ReadAllLines("E:\Abfrage.txt").GetLast(5).ToArray()
    13. 'liefert mir die letzten 5 Zeilen
    14. DataGridView1.Rows.Add(line.Split(";"))
    15. Next
    16. End Sub
    17. End Class


    gruss
    kasi
    Na ja, dann müssen aber alle "Abschnitte" immer genau die gleiche Länge haben....
    Wenn es mal nicht "Information" sondern z.B. "Fehler" heißt funktioniert dein Beispiel ja nicht mehr...

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor
    Also ich gestalte meine Logs immer so: [Zeitstempel][LogTyp][Klasse.Funktion][LogNachricht]
    Das Format lässt sich dann recht einfach mittels Regex, Split oder IndexOf verarbeiten. Für die Splitvariante wird die Führende und endende Klammer [] mittels Trim entfernt und dann bei ][ gesplittet.

    Spoiler anzeigen

    C#-Quellcode

    1. static void Main(string[] args)
    2. {
    3. string myLog = "[01.01.2020 08:30:15][DEBUG][Programm.Main][HelloWorld]";
    4. char[] trimChars = new char[] { '[', ']' };
    5. string[] parts = myLog.Trim(trimChars).Split("][");
    6. foreach (string s in parts)
    7. Console.WriteLine(s);
    8. Console.ReadLine();
    9. }
    Wie in post#3 schon gesagt: Sehr wichtig für eine komfortable Auswertung ist, dass der Zeitstempel sortierbar ist, also sowas:string myLog = "[2020-08-06 08:30:15][DEBUG][Programm.Main][HelloWorld]";.
    Das bringt nicht nur die chronologische Abfolge zu Gesicht, sondern ermöglicht auch zb. Filter zu setzen, die einen Zeitbereich eingrenzen.

    Aber alles andere aus post#3 ist ja ebenfalls inzwischen wohl untergegangen - es wird wohl ein nicht besonders leistungsfähiger LogViewer werden (oder aber überdimensional aufwändig, weil auf ungeeignete Technologien setzend).
    Hallo noch einmal,

    schuldigt für die etwas späte Antwort, aber ich musste erst einmal alles durchlesen und auch verstehen, was mir selbst wichtig ist.
    Allerdings danke ich natürlich jedem einzelnen für seinen Beitrag und für die super Lösungsfindungen.
    Ein Mix aus allem hat es dann soweit auch möglich gemacht.

    florian03 schrieb:

    Da musst du dann - wie du es auch am Anfang gemacht hast - ein Objekt mit Namen inistalisieren und dann 2 mal die SubItems.Add Methode aufrufen (denk dran, dass du dann auch 3 Spalten in deinem ListView benötigst)

    Kurz bevor du die Antwort verfasst hattest, habe ich dies bereits wieder so umgesetzt. So etwas dachte ich mir schon, dennoch Danke!

    florian03 schrieb:

    Das ist das Problem mit dem Leerzeichen als Trennzeichen. Wie man es beim 3. Element ignorieren kann weiß ich nicht. Da müsstest du dann wieder "basteln". Hab ich mal kurz gemacht

    Da bin ich noch dran, allerdings habe ich dies auch erst einmal soweit hinbekommen und den Index der Logs noch einmal angepasst um dies besser filtern zu können.

    Kasi schrieb:

    ich habe hier ein Bsp. mit Datagridview

    Vielen Dank Kasi, ich werde mir auch dies noch einmal durchprobieren um die beste Lösung dafür zu finden.

    Eierlein schrieb:

    Oder ganz einfach mit Substring.

    Danke Eierlein auch für deinen Beitrag.

    ErfinderDesRades schrieb:

    Aber alles andere aus post#3 ist ja ebenfalls inzwischen wohl untergegangen

    @ErfinderDesRades, nicht so schnell beleidigt sein, ich hatte dir nach deinem obigen Post eine persönliche P/N geschrieben, somit - Nein - das ist nicht untergegangen, ich muss nur auch leider einer normalen Arbeit nachgehen und habe nur bedingt Zeit. Ich habe mir das zu herzen genommen und werde das ganze auch versuchen besser umzusetzen um ein besseres Logging auf die Reihe zu bekommen. Wenn du dort noch detaliert Tipps und Tricks hast - außer den genannten - wäre ich dir natürlich sehr dankbar.

    Vielen Dank noch einmal im allgemeinen.

    Das eigentliche Thema sehe ich dort somit als erledigt.

    Gruß Spike

    ErfinderDesRades schrieb:

    Ups - MIst! PN - da schwächel ich immer, das mussich mal anners organisieren.
    Und dann war ich Urlaub.
    Wie dem auch sei - in PN gehe ich nur ausnahmsweise detailliert auf iwas ein, weil imo musses einen Grund geben, dass etwas in PN zu verhandeln ist und nicht im Forum.


    Das wäre super, ich versuche dahingehend nur zu verstehen, wie ich das ganze auch verbessern kann bzw. möchte ja auch lernen wie man so etwas - wie mit den Loggs - am besten aufbauen kann.

    Das es etwas mehr Text war, habe ich dir dahingehend eine PN geschickt, da es ja zu dem eigentlichen Thema hier nicht passt.

    Vielen Dank, war natürlich nicht böse gemeint ;)

    Spike