Satzverarbeitung in VB

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

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von petaod.

    Satzverarbeitung in VB

    Der Jahreswechsel ist zwar schon ein paar Tage her. Trotzdem erst mal ein "Gutes Neues Jahr 2016" an alle Forumsteilnehmer.

    Ich bekomme (von irgend woher) eine Text Datei mit Daten, die folgenden Aufbau hat (die trennenden Leerzeichen sind mit abgespeichert):

    Quellcode

    1. ---Date--- --Time-- X:\ accounted cnterrs d-disk dirs files t-disk dirs files
    2. 06.01.2016 08:53:58 116,7 GB 116,4 GB 0 2,3 GB 1.821 21.119 114,1 GB 3.805 27.277
    3. 07.01.2016 08:55:17 116,7 GB 116,4 GB 0 2,3 GB 1.840 21.210 114,1 GB 3.832 27.285
    4. ...


    Diese Daten würde ich jetzt gern in meinem VB Programm auswerten. Wie mache ich das denn ?

    In den "alten" prozeduralen Sprachen wie COBOL, PLI oder ASSEMBLER gibt es etwas, das sich COPY-Book nennt, das ist eine Struktur, die den Satzaufbau beschreibt und die im Programm als Referenz verwendet werden kann. Ich denke mal, das Pendant dazu im VB ist eine STRUCTURE Anweisung ist. Aber wie muss ich jetzt die Elemente deklarieren?

    Mir fällt zunächst der Variablentype STRING ein. Aber der ist variabel lang .... ich brauche aber hier Strings fester Länge ... oder noch besser numerische Felder mit festem Aufbau.

    Natürlich könnte ich mir mit .SUBSTRING(start, length) die Daten aus dem Eingabesatz heraus klamüsern. Aber das ist ja wohl nicht so ganz im Sinne der schönen neuen Welt der unbegrenzten Objektorientierung.

    Kann mir jemand auf die Sprünge helfen?

    LG
    Peter
    Das ist eine Tabelle von Datensätzen, um solch zu verarbeiten musst du dir als erstes eine Klasse erschaffen, die für jede Spalte eine Property hat.

    Dann kann man obige Tabelle zeilenweise durchgehen, und pro Zeile ein Objekt der Datensatz-Klasse erzeugen, und zB. einer List(Of DatensatzKlasse) zufügen.



    Es gibt auch Abkürzungen, die sogar wesentlich leistungsfähigere Unterstütung für die Weiter-Verarbeitung bieten.

    ZB kann man drauf verzichten, die Datensatz-Klasse selbst zu programmieren, und legt stattdessen ein typisiertes Dataset an, und darin eine DataTable, mit geeigneten Spalten.
    Dann programmiert der Dataset-Designer für dich die Datensatzklasse (in Form einer typisierten DataRow), und stattet sie von vornherein mit enormen Möglichkeiten der Weiterverarbeitung aus.



    Aber wenn du noch nie eine eigene Klasse geproggt hast, ist das vlt. erstmal dran, bevor man sich an typDatasets ranmacht.

    Kannst du eine Klasse programmieren? Menü-Projekt-Hinzufügen-Klasse - und public muss sie sein.
    Und darin ein paar Properties anlegen?
    Die Sache mit der Klasse würde ich gern probieren.

    Voilá

    VB.NET-Quellcode

    1. Public Class clsStatistics
    2. Property StatDateTime As Date 'Date and time of entry
    3. 'Drive
    4. Property StatTotal As Decimal 'Total GB
    5. Property StatAccounted As Decimal 'Accounted GB
    6. Property StatErrors As Integer 'Number of authorization errors
    7. 'Partition 1
    8. Property StatTotal1 As Decimal 'Total GB
    9. Property StatDirs1 As Long 'Number of directories
    10. Property StatFiles1 As Long 'Number of files
    11. 'Partition 2
    12. Property StatTotal2 As Decimal 'Total GB
    13. Property StatDirs2 As Long 'Number of directories
    14. Property StatFiles2 As Long 'Number of files
    15. End Class


    Wie geht es weiter? Ich nehme an, dass ich jetzt einen Konstruktor schreiben muss ... aber da liegt der Hase im Pfeffer ... denn wie übertrage ich die Daten aus dem gelesenen Satz in die einzelnen Properties?

    LG
    Peter

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

    sehr gut! Nur mach die Properties Public

    Und dann brauchemer eine Methode zum Einlesen.
    Hier hat man 2 Möglichkeiten: Entweder die Methode bekommt den Dateinamen, und gibt eine List(Of clsStatistics) zurück, oder sie bekommt sowohl die Liste als auch den Dateinamen, und befüllt einfach nur die Liste. Dann braucht sie nix zurückzugeben.
    In Databinding-Szenarien bevorzuge ich letzteres, aber deine Entscheidung.
    Schreib entweder die eine oder die andere Methode - wie du willst.
    Innen rein muss noch nix, nur die anzugebenden Parameter, und ggfs. der Rückgabetyp sind erstmal festzulegen.
    Ok, dann übergebe ich mal den File und die Liste

    VB.NET-Quellcode

    1. Imports System.IO
    2. Public Class clsStatistics
    3. Public Property StatDateTime As Date 'Date and time of entry
    4. 'Drive
    5. Public Property StatTotal As Decimal 'Total GB
    6. Public Property StatAccounted As Decimal 'Accounted GB
    7. Public Property StatErrors As Integer 'Number of authorization errors
    8. 'Partition 1
    9. Public Property StatTotal1 As Decimal 'Total GB
    10. Public Property StatDirs1 As Long 'Number of directories
    11. Public Property StatFiles1 As Long 'Number of files
    12. 'Partition 2
    13. Public Property StatTotal2 As Decimal 'Total GB
    14. Public Property StatDirs2 As Long 'Number of directories
    15. Public Property StatFiles2 As Long 'Number of files
    16. Sub New(StatFullFilename As String, StatList As List(Of clsStatistics))
    17. Try
    18. Dim sr As StreamReader = New StreamReader(StatFullFilename)
    19. Do While (sr.Peek() <= 0)
    20. Dim strRecord As String = sr.ReadLine
    21. 'Process record ***to do ***
    22. Loop
    23. sr.Close()
    24. Catch
    25. 'Error handling ***to do***
    26. End Try
    27. End Sub
    28. End Class


    Bleibt die Frage, wie ich jetzt die Daten aus strRecord in meine Properties bekomme.

    Peter329 schrieb:

    bekomme
    So was:

    VB.NET-Quellcode

    1. Dim txt = "06.01.2016 08:53:58 116,7 GB 116,4 GB 0 2,3 GB 1.821 21.119 114,1 GB 3.805 27.277"
    2. txt = txt.Replace("GB", "").Replace(",", ".")
    3. Dim parts() = txt.Split(New Char() {" "c}, StringSplitOptions.RemoveEmptyEntries)

    Nun musst Du nur noch einzeln die Strings konvertieren und Deinen Properties zuordnen.
    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!
    @Peter: Nein, ich meinte eine Methode, und die sollte auch nicht in der clsStatidingsbums-Klasse drinne sein. Du hast da eine Sub New geschrieben, einen Konstruktor - das wird nicht funzen.

    Andererseits - wenn man die Methode Shared macht, kann man sie auch in der Klasse selbst anlegen:

    VB.NET-Quellcode

    1. Public Class clsStatistics
    2. Public Property StatDateTime As Date 'Date and time of entry
    3. 'Drive
    4. Public Property StatTotal As Decimal 'Total GB
    5. Public Property StatAccounted As Decimal 'Accounted GB
    6. Public Property StatErrors As Integer 'Number of authorization errors
    7. 'Partition 1
    8. Public Property StatTotal1 As Decimal 'Total GB
    9. Public Property StatDirs1 As Long 'Number of directories
    10. Public Property StatFiles1 As Long 'Number of files
    11. 'Partition 2
    12. Public Property StatTotal2 As Decimal 'Total GB
    13. Public Property StatDirs2 As Long 'Number of directories
    14. Public Property StatFiles2 As Long 'Number of files
    15. Public Shared Sub FillFromFile(StatFullFilename As String, StatList As List(Of clsStatistics))
    16. Try
    17. Dim sr As StreamReader = New StreamReader(StatFullFilename)
    18. Do While (sr.Peek() <= 0)
    19. Dim strRecord As String = sr.ReadLine
    20. 'Process record ***to do ***
    21. Loop
    22. sr.Close()
    23. Catch
    24. 'Error handling ***to do***
    25. End Try
    26. End Sub
    Also ich hätte gedacht, die Methode steht in ieinem Form, und wird per ButtonClick aufgerufen, aber kann man auch so machen, und das Form freut sich, wenn weniger Kram drin rumfährt.

    Ach - und bitte kein Error-Handling, solange das nicht alles fix und fertig ist.
    Solange etwas unfertig ist, dürfen keine Exceptions gefangen werden. Denn gefangene Exceptions erreichen den Entwickler nicht. Sie müssen ihn aber erreichen, sonst kann er den Code ja nicht verbessern.
    Quasi bei unfertigem Code ist der Entwickler das Exception-Handling.
    Wenn wolle gugge TryCatch ist ein heißes Eisen

    jdfs. dein nächster schritt kann sein, diese Methode nun aufzurufen.
    Wie du siehst, brauchst du dafür einen Dateinamen, und eine List(Of clsStatidingsbums).
    Und vmtl. wirds von einem ButtonClick-Handler aufgerufen, aus einem Form heraus.

    Und nu bin ich gespannt, ob du auch eine Shared Methode richtig aufrufen kannst.

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

    Erst mal Danke auch an die beiden anderen Ratgeber. Lasst mich aber zunächst die Lösung von EDR zu Ende führen.

    Also ich hab das jetzt wie folgt abgeändert:

    VB.NET-Quellcode

    1. Public Shared Sub StatLoadFile(StatFullFilename As String, StatList As List(Of clsStatistics))
    2. Try
    3. Dim sr As StreamReader = New StreamReader(StatFullFilename)
    4. Do While (sr.Peek() <= 0)
    5. Dim strRecord As String = sr.ReadLine
    6. 'Process record
    7. Loop
    8. sr.Close()
    9. Catch ex As Exception
    10. 'Error handling ***to do***
    11. MessageBox.Show(ex.Message)
    12. End Try
    13. End Sub


    Jetzt verschwinden keine Exceptions mehr.

    Den Aufruf der Shared Sub kriege ich aber leider nicht gebacken.

    VB.NET-Quellcode

    1. Private Sub cmdStatLoad_Click(sender As System.Object, e As System.EventArgs) Handles cmdStatLoad.Click
    2. Dim StatList As New List(Of clsStatistics)
    3. Dim StatFileName as String = DATASTATSFILENAME
    4. LoadStatFile(StatFileName, StatList)
    5. End Sub


    Ich bekomme die Fehlermeldung:

    Quellcode

    1. "LoadStatFile" wurde nicht deklariert.


    Verstehe ich nicht ... das Ding ist doch als Public deklariert. Das muss wohl irgendwie mit der Instanzierung zusammen hängen.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Peter329“ ()

    Nein, das ist grad der Witz an Shared Methoden im Gegensatz zu normalen Objekt-Methoden:
    Objekt-Methoden gehören dem einzelnen Objekt, und um sie aufzurufen, muss man logischerweise ein Objekt (instanziert) haben.

    Shared-Methoden gehören nicht dem einzelnen Objekt, sondern das ist einfach irgendwelcher Code, der einfach in den Klassen-Code hineingeschrieben wurde.
    Naja, ganz beliebig ists nicht, also ist empfehlenswert, thematisch passenden Shared Code anzusiedeln.
    Ein weiterer Shared-Effekt ist, dass Objekte der Klasse bevorzugten Zugriff auf diesen Shared Code haben.
    Also wenn du eine Private Shared Methode in eine Klasse reinmachst, dann ist die von aussen unsichtbar, aber für jedes Objekt dieser Klasse ist die Methode verfügbar. Sie teilen sich die Methode (to share).

    Aber ist hier nicht von Belang - deine Shared Methode ist Public, und deine Objekte sollen sie garnet zugreifen.
    Sondern von Aussen soll zugegriffen werden.
    Und beim Zugriff auf Shared Member gibt man nun statt eines Objektes den allgemeinen Klassen-Namen an:

    VB.NET-Quellcode

    1. Private Sub cmdStatLoad_Click(sender As System.Object, e As System.EventArgs) Handles cmdStatLoad.Click
    2. Dim StatList As New List(Of clsStatistics)
    3. Dim StatFileName = DATASTATSFILENAME
    4. clsStatistics.StatLoadFile(StatFileName, StatList)
    5. End Sub
    und schon geht das.



    Was anneres:
    Ich wiederhole mich nicht gerne, aber nun mussichs doch: Mach blos den TryCatch da weg!
    Oder findest du meine Begründung, warum er Gift ist, nicht stichhaltig?
    Dann lass uns darüber reden, oder lies das verlinkte Tut oder whatever. Nur feedback-los ignoriert werden, wenn ich wesentliches von mir gebe, das geht mir schnell gegen den Strich.

    Peter329 schrieb:

    wurde nicht deklariert
    In welcher Klasse?
    Bei Shared Prozeduren in anderen Klassen musst Du den Klassennamen davorschreiben.
    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!
    Ok, das verstehe ich. Die Methode ist zwar auch ohne Instanzierung präsent, da als Public Shared definiert ... aber der Aufruf muss mitteilen, aus welcher Klasse das Ding denn zu nehmen ist. Jetzt ist der Aufruf fehlerfrei.

    Den Try-Catch-Block hab ich entfernt.

    Damit werden jetzt in der Shared Procedure die Sätze der Reihe nach gelesen.

    Wie geht es denn jetzt weiter ...

    Peter329 schrieb:

    gelesen
    Generiere Daten-Instanzen draus.
    Ich würde die Einlese-Funktion wie folgt umbauen:
    von

    VB.NET-Quellcode

    1. Public Shared Sub StatLoadFile(StatFullFilename As String, StatList As List(Of clsStatistics))
    nach

    VB.NET-Quellcode

    1. Public Shared Function StatLoadFile(StatFullFilename As String) As List(Of clsStatistics)
    und dann arbeite mit diesen Instanzen.
    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!
    naja, für jeden Satz-String musste als erstes ein clsStatDingsbums erstellen.
    Dann splitteste den Satz in Teil-Strings, und die Einzelteile musste auf die Properties deines clsStatDingsbums verteilen.
    Bei Properties vom Typ String ists einfach, kannste direkt zuweisen.
    Bei anderen Properties musste bisserl Hirnschmalz anwenden.

    Etwa ein Satz hat ja u.a. die Spalten Datum und Zeit (Date und Time). Das kann man gut zusammenfassen in eine einzige Property vom Typ DateTime.

    Und so weiter und so fort.

    Am besten fängste mit den String-Properties an - achmist - haste ja keine.

    Das nächst-Einfache sind Teilstrings ohne Massangaben, also die Spalten dirs oder files - die kann man einfach mit Long.Parse() in einen Long konvertieren.

    Dann ist die Liste immerhin schonmal befüllt, wenn auch nur mit unvollständigen clsStatDingsbumsens.

    Edit
    @Rod: Ja, das ist die andere Alternative - hatte ich ja eingangs ihm zur Auswahl gestellt: Entweder eine vorhandene Liste befüllen, oder eine neu Liste generieren. Damals hatte er sich fürs befüllen entschieden.

    ErfinderDesRades schrieb:

    Alternative
    Jou.
    Ich hab gerade bei mir solch Prozedere genau so umgestellt, deswegen kam ich drauf.
    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, so langsam verstehe ich das ... egal, ob ich die Liste mit übergebe oder als Ergebnis zurückliefere ... in jedem Fall komme ich nicht darum herum den Eingabesatz aufzudröseln. So altmodische Sachen wie COPY-Strecken gibt es eben nicht mehr in der schönen neuen Welt.

    Zum Aufdröseln hat RFG vorgeschlagen mit .Replace und .Split zu arbeiten. Das ist dahingehend gefährlich, weil sich Dateninhalte schnell ändern können. Z.b. erhalte ich morgen statt "GB" mal "MB" und schon fällt meine schöne Routine auf die Nase.

    Alternativ gibt es die von US4711 genannte Methode. Hier werden Offsets und Längen spezifiziert. Auch die können sich ändern, aber man ist wenigstens von den Feldinhalten unabhängig. Und es ist allemal besser als mit .Substring zu werkeln, weil die Parameter eben nur an einer Stelle eingetragen werden.

    So wie ich das jetzt sehe, dient dient die Klassenbildung nur dazu, um die aufgedröselten Parameter in Properties einzustellen. Am eigentlichen Problem der Aufbereitung von Variablen aus einer festen Satzstruktur ändert sich damit aber nix. Das ging schon vor 40 Jahren im COBOL einfacher.

    Herzlichen Dank an alle Ratgeber. Ich hab ein Menge gelernt. Jetzt mach ich mich mal dran, die Sache umzusetzen!

    LG
    Peter

    Peter329 schrieb:

    So wie ich das jetzt sehe, dient dient die Klassenbildung nur dazu, um die aufgedröselten Parameter in Properties einzustellen. Am eigentlichen Problem der Aufbereitung von Variablen aus einer festen Satzstruktur ändert sich damit aber nix.
    "Nur" ist gut - ohne diese Vorbereitungen könntest du überhaupt nicht anfangen, einen Datensatz auszuwerten.

    Ist ein klassischer Anfänger-Fehler: Die wollen immer was auslesen, haben aber garnix, wohin sie es einlesen könnten. Solch endet in ziemlich grotesken "Lösungen".

    Tja, und wenn deine Daten so unterschiedlich daherkommen, ja, solch ist oft ein schwieriges Problem. (unter "fester Satzstruktur" verstehe ich ein bischen was anderes, als was du da am Hals hast).

    Ein recht mächtiges Werkzeug zur String-Analyse ist auch Regex - aber das ist auch eine Wissenschaft für sich.
    Meine Empfehlung: Lass dir doch einfach bischen helfen.



    Und nochwas: Deine Benamung ist stark verbesserungsfähig. Auch dazu könnte ich noch mehr erzählen, bei Interesse.
    Hej @Peter329.

    Peter329 schrieb:

    Zum Aufdröseln hat RFG vorgeschlagen mit .Replace und .Split zu arbeiten.

    Das würde ich dir auch vorschlagen.

    Peter329 schrieb:

    erhalte ich morgen statt "GB" mal "MB" und schon fällt meine schöne Routine auf die Nase

    Da könnte man die Einheit erstmal ignorieren und dann am Ende mit Regex entfernen. Wenn du dich da nicht rantraust, helfen wir (oder zumindest ich) dir da gerne.

    ErfinderDesRades schrieb:

    Deine Benamung ist stark verbesserungsfähig

    Benamung :thumbsup:

    Grüße
    Väinämö

    ErfinderDesRades schrieb:


    Meine Empfehlung: Lass dir doch einfach bischen helfen.


    Also, ich schätze ja alle Ratschläge. Und das meine ich Ernst. Aber ich versuche halt auch Dinge einzuordnen und zu bewerten. Soweit das halt mit meinen beschränkten Kenntnissen möglich ist. :)

    Mich würde jetzt wirklich mal interessieren, was an der nachfolgenden "hausbackenen"Lösung des Problems so grundlegend falsch wäre (die Konvertierung der Daten habe ich der Einfachheit halber mal außen vor gelassen):

    VB.NET-Quellcode

    1. Public Sub StatLoadFile2(StatFullFilename As String)
    2. Dim sr As StreamReader = New StreamReader(StatFullFilename)
    3. Do While (sr.Peek() > 0)
    4. Dim strRecord As String = sr.ReadLine
    5. 'Process record
    6. strRecord = strRecord.PadRight(130, " "c)
    7. Dim strDate As String = strRecord.Substring(0, 10)
    8. Dim strTime As String = strRecord.Substring(11, 8)
    9. Dim strTotal As String = strRecord.Substring(22, 10)
    10. Dim strAccounted As String = strRecord.Substring(33, 10)
    11. Dim strCntErrors As String = strRecord.Substring(44, 10)
    12. Dim strTotal1 As String = strRecord.Substring(55, 10)
    13. Dim strCntDirs1 As String = strRecord.Substring(66, 10)
    14. Dim strCntFiles1 As String = strRecord.Substring(77, 10)
    15. Dim strTotal2 As String = strRecord.Substring(88, 10)
    16. Dim strCntDirs2 As String = strRecord.Substring(99, 10)
    17. Dim strCntFiles2 As String = strRecord.Substring(110, 10)
    18. ...
    19. Loop
    20. sr.Close()
    21. End Sub


    Was bringt mir denn der Aufwand für die Klassenbildung, wenn ich letzten Endes den Satz doch "aufdröseln" muss. Das wäre - nach meinem Verständnis - nur von Vorteil, wenn solche Klassen MEHRFACH benötigt würden. Aber diese Offset Bildung ist eben genau für DIESEN Satz anwendbar und entzieht sich damit einer Verallgemeinerung.

    Also ich sehe keine "elegante" Lösung, wie man Sätze mit festem Aufbau entgegennehmen kann. Das könnnen die prozeduralen Sprachen dann wesentlich besser.

    LG
    Peter
    ich find deinen Code garnet so schlecht, also das Aufdröseln gelingt schonmal, nur hast du jetzt ja noch nur lauter Strings (statt zweckgemässer Datentypen, wie DateTime und Long).

    Ausserdem hast du die Datenklasse nu wieder verworfen - ja, was willst du nun mit diesen Strings anfangen?
    Du hast keine List(Of clsStatisDingsbums), wo du sie in Datensätze zusammengefasst ablegen könntest für die weitere Verwendung :/

    Das ist genau der genannte Anfänger-Fehler: Die Datei wird fabelhaft ausgelesen, aber es gibt nichts, wo die Daten dann hineingetan werden könnten.

    Also ich empfehle, die Datenklasse wieder anzuschaffen.
    Und das ist vlt. eine Idee: du könntest alle derer Properties ja zu strings machen - dann brauchst du erstmal nichts zu konvertieren.

    Die List(Of clsStatDingsbums) kannst du dann auch ganz einfach anzeigen - ein DataGridView aufs Form und:

    VB.NET-Quellcode

    1. dim lst= new List(Of clsStatDings)
    2. clsStatDings.FillFromFile(fileName)
    3. datagridView1.DataSource=lst


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