1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Zeilenweises Prüfen einer Datei mit RegExp

Eine Datei mit RegeExp zeilenweise durchsuchen und die Resultate verwerten

  1. Yaslaw
    Intro
    RegExp-Erfahrung ist eine Voraussetzung für dieses Tutorial. Ich gehe nicht weiter auf die Regulären Ausdrücke und die Grundfunktionalität von VBScript.RegExp ein.

    Ich bekomme immer wieder Text-Dateien, die irgend ein Report aus einem System darstellen. Leider sind diese dann nicht im csv- oder einem anderen Sinnvollen Format vorhanden. Also muss man mit einem Parser zuerst herausfinden.
    Analyse

    Quelldaten
    Die Dateien sehen zum Beispiel so aus
    Code (Text):
    1. Date    03/11/2017
    2. User    Max Muster
    3.  
    4. Willkommen Max Muster  und Balh und sowieso
    5. viele unütze Zeilen
    6.  
    7.  
    8. nicht wahr?
    9.  
    10. Account         ABC-123
    11.  
    12. time    ID      what        price       local    
    13. 1130    12      Coffee      1.25 EUR    1.25 EUR
    14.                 Div.        3.26 EUR    3.26 EUR
    15.  
    16. Total   ABC-123                         4.51 EUR
    17.  
    18. Account         DEF-456
    19.  
    20. time    ID      what        price       local
    21. 1145    25      xy          5.25 CHF    4.90 EUR
    22. 1146    26      dfr         4.00 USD    3.70 EUR
    23.                 okhgg       1.50 USD    1.39 EUR
    24. 1543    27      oppojkj     3.00 EUR    3.00 EUR
    25. Total   DEF-456                        12.99 UER
    26.  
    27. Viel unnötiger Text etc.
    Die ersten 2 Zeilen sind Grund-Informationen. Die will ich nachher in meiner DB in jeder Zeile sehen
    Dann kommt BlahBlah Blah.
    Gefolgt von einem Account. Der Interessiert mich.
    Dann kommt eine Tabelle. Da will ich jeden Eintrag haben. Die Zeit und ID soll sich wiederholen.

    Gewünschtes Resultat
    So soll mein Resultat aussehen
    Code (Text):
    1. date       | time  | user       | account | id | what    | product_price | product_currency | local_price | local_currency
    2. -------------------------------------------------------------------------------------------------------------------------
    3. 03/11/2017 | 11:30 | Max Muster | ABC-123 | 12 | Coffee  |          1.25 | EUR              |        1.25 | EUR
    4. 03/11/2017 | 11:30 | Max Muster | ABC-123 | 12 | Div.    |          3.26 | EUR              |        3.26 | EUR
    5. 03/11/2017 | 11:45 | Max Muster | DEF-456 | 25 | xy      |          5.25 | CHF              |        4.90 | EUR
    6. 03/11/2017 | 11:46 | Max Muster | DEF-456 | 26 | dfr     |          4.00 | USD              |        3.70 | EUR
    7. 03/11/2017 | 11:46 | Max Muster | DEF-456 | 26 | okhgg   |          1.50 | USD              |        1.39 | EUR
    8. 03/11/2017 | 15:43 | Max Muster | DEF-456 | 27 | oppojkj |          3.00 | EUR              |        3.00 | EUR
    Lösung

    RegExp
    Dann gehen wir mal ans Werk. Die einzelnen Zeilen werden mittels RegExp gescannt und ausgewertet.
    Folgende Zeilen brauche ich

    Date/User/Account Wert
    1130 12 Coffee 1.25 EUR 1.25 EUR


    Das gibt also 2 verschiedene RegExp. Da RegExp am schnellsten ist, wenn es nicht jedes mal initialisiert werde muss, Schreibe ich dafür Private Properties mit einem Static Object

    Code (Visual Basic):
    1. 'Date    03/11/2017
    2. 'Ausgabe: Date/User/Account, Wert
    3. Private Property Get rxSingle() As Object
    4.     Static rx As Object
    5.     If rx Is Nothing Then
    6.         Set rx = CreateObject("VBScript.RegExp")
    7.         rx.pattern = "^(Date|User|Account)\s+(.+)"
    8.         rx.IgnoreCase = True
    9.     End If
    10.     Set rxSingle = rx
    11. End Property
    12.  
    13. '1130    12      Coffee      1.25 EUR    1.25 EUR
    14. '                Div.        3.26 EUR    3.26 EUR
    15. 'Ausgabe: time, ID, what, product_price, product_currency, local_price, local_currency
    16. Private Property Get rxData() As Object
    17.     Static rx As Object
    18.     If rx Is Nothing Then
    19.         Set rx = CreateObject("VBScript.RegExp")
    20.         rx.pattern = "(\d{4})?\s+(\d+)?\s+(.+?)\s+(\d+\.\d{2})\s+([a-z]{3})\s+(\d+\.\d{2})\s+([a-z]{3})"
    21.         rx.IgnoreCase = True
    22.     End If
    23.     Set rxData = rx
    24. End Property
    rxSingle Gibt indem Fall ein Treffer zurück, der 2 SubMatches beinhaltet. Der Name der Variable (Date, User oder Account) und den dazugehörigen Wert.
    rxData wertet die Tabellenzeilen aus und gibt alle Feldinhalte zurück

    Funktion
    Als nächstes können wir bereits die eigentliche Funktion schreiben.
    Darin öffne ich über FileSystemObject die Datei als Stream und iteriere sie Zeilenweise
    Code (Visual Basic):
    1. Public Sub readMyFile()
    2.     Dim fso As Object
    3.     Dim stream As Object
    4.     Dim line As String
    5.  
    6.     'Datei als Stream öffnen
    7.    Set fso = CreateObject("Scripting.FileSystemObject")
    8.     Set stream = fso.OpenTextFile("C:\_TMP\test.txt", ForReading)
    9.  
    10.     'Zeilenweises abarbeiten
    11.    Do While Not stream.AtEndOfStream
    12.         line = stream.ReadLine
    13.         '//TODO: Zeile auswerten
    14.    loop
    15.     stream.Close
    16. End Sub
    Noch kurz die Variablen für die Auswertung definieren
    Code (Visual Basic):
    1.     Dim dateVal As Date, timeVal As Date
    2.     Dim user As String, account As String, what As String, product_currency As String, local_currency As String
    3.     Dim id As Long
    4.     Dim product_price As Double, local_price As Double
    Jetzt kommt die Zeilenauswertung mit den rx-Properties.
    Für die einfachen Werte. Date, User, Account. Da die Information, um welches Attribut es sich handelt, im ersten SubMatch befindet, wird dieses mittels Select Case ausgewertet
    Code (Visual Basic):
    1. If rxSingle.Test(line) Then
    2. 'Den Single-Wert setzen
    3.    Set sm = rxSingle.execute(line)(0).subMatches
    4.     Select Case LCase(sm(0))
    5.         Case "date":    dateVal = sm(1)
    6.         Case "user":    user = sm(1)
    7.         Case "account": account = sm(1)
    8.     End Select
    9. End If
    Und die Datenzeilen.
    Hier entspricht jeder SubMatch einer Variable. Die 2 Ersten SubMatches können leer sein, wie wir in den Daten gesehen haben. In dem Fall dürfen die Variablen nicht überschrieben werden
    Code (Visual Basic):
    1. If rxData.Test(line) Then
    2. 'Eine Zeile auswerten
    3. 'time, ID, what, product_price, product_currency, local_price, local_currency
    4.    Set sm = rxData.execute(line)(0).subMatches
    5.     'Die ersten 2 Spalten können leer sein. Dann soll der Wert nicht mit Leer überschrieben werden
    6.    If sm(0) <> Empty Then timeVal = timeValue(format(sm(0), "00:00"))
    7.     If sm(1) <> Empty Then id = CLng(sm(1))
    8.     what = sm(2)
    9.     product_price = CDbl(sm(3))
    10.     product_currency = sm(4)
    11.     local_price = CDbl(sm(5))
    12.     local_currency = sm(6)
    13.     '//TODO: Mit den Variablen irgendwas machen
    14. End If
    Und schon haben wir alle Daten zusammen. Fürs Beispiel gebe ich sie nur im Direktfenster aus. In Der Praxis könnte man diese jetzt in eine DB speichern oder einer anderen Methode übergeben
    Code (Visual Basic):
    1. 'Die Daten ausgeben. Hier nur per debug.print. Man kann damit jetzt aber machen was man will
    2. Debug.Print dateVal, timeVal, user, account, id, what, product_price, product_currency, local_price, local_currency
    Vollständige Lsöung
    Hier der fertige Code
    Code (Visual Basic):
    1. Public Sub readMyFile()
    2.     Dim fso As Object
    3.     Dim stream As Object
    4.     Dim line As String
    5.     Dim sm As Object
    6.     Dim dateVal As Date, timeVal As Date
    7.     Dim user As String, account As String, what As String, product_currency As String, local_currency As String
    8.     Dim id As Long
    9.     Dim product_price As Double, local_price As Double
    10.  
    11.     'Datei als Stream öffnen
    12.    Set fso = CreateObject("Scripting.FileSystemObject")
    13.     Set stream = fso.OpenTextFile("C:\_TMP\test.txt", ForReading)
    14.  
    15.     'Zeilenweises abarbeiten
    16.    Do While Not stream.AtEndOfStream
    17.         line = stream.ReadLine
    18.      
    19.         If rxSingle.Test(line) Then
    20.         'Den Single-Wert setzen
    21.            Set sm = rxSingle.execute(line)(0).subMatches
    22.             Select Case LCase(sm(0))
    23.                 Case "date":    dateVal = sm(1)
    24.                 Case "user":    user = sm(1)
    25.                 Case "account": account = sm(1)
    26.             End Select
    27.         ElseIf rxData.Test(line) Then
    28.         'Eine Zeile auswerten
    29.        'time, ID, what, product_price, product_currency, local_price, local_currency
    30.            Set sm = rxData.execute(line)(0).subMatches
    31.             'Die ersten 2 Spalten können leer sein. Dann soll der Wert nicht mit Leer überschrieben werden
    32.            If sm(0) <> Empty Then timeVal = timeValue(format(sm(0), "00:00"))
    33.             If sm(1) <> Empty Then id = CLng(sm(1))
    34.             what = sm(2)
    35.             product_price = CDbl(sm(3))
    36.             product_currency = sm(4)
    37.             local_price = CDbl(sm(5))
    38.             local_currency = sm(6)
    39.             'Die Daten ausgeben. Hier nur per debug.print. Man kann damit jetzt aber machen was man will
    40.            Debug.Print dateVal, timeVal, user, account, id, what, product_price, product_currency, local_price, local_currency
    41.         End If
    42.     Loop
    43.     stream.Close
    44. End Sub
    45.  
    46. 'Date    03/11/2017
    47. 'Ausgabe: Date/User/Account, Wert
    48. Private Property Get rxSingle() As Object
    49.     Static rx As Object
    50.     If rx Is Nothing Then
    51.         Set rx = CreateObject("VBScript.RegExp")
    52.         rx.pattern = "^(Date|User|Account)\s+(.+)"
    53.         rx.IgnoreCase = True
    54.     End If
    55.     Set rxSingle = rx
    56. End Property
    57.  
    58. '1130    12      Coffee      1.25 EUR    1.25 EUR
    59. '                Div.        3.26 EUR    3.26 EUR
    60. 'Ausgabe: time, ID, what, product_price, product_currency, local_price, local_currency
    61. Private Property Get rxData() As Object
    62.     Static rx As Object
    63.     If rx Is Nothing Then
    64.         Set rx = CreateObject("VBScript.RegExp")
    65.         rx.pattern = "(\d{4})?\s+(\d+)?\s+(.+?)\s+(\d+\.\d{2})\s+([a-z]{3})\s+(\d+\.\d{2})\s+([a-z]{3})"
    66.         rx.IgnoreCase = True
    67.     End If
    68.     Set rxData = rx
    69. End Property
    Und die Ausgabe im Direktfenster
    Code (Text):
    1. 03.11.2017    11:30:00      Max Muster    ABC-123        12           Coffee         1.25         EUR            1.25         EUR
    2. 03.11.2017    11:30:00      Max Muster    ABC-123        12           Div.           3.26         EUR            3.26         EUR
    3. 03.11.2017    11:45:00      Max Muster    DEF-456        25           xy             5.25         CHF            4.9          EUR
    4. 03.11.2017    11:46:00      Max Muster    DEF-456        26           dfr            4            USD            3.7          EUR
    5. 03.11.2017    11:46:00      Max Muster    DEF-456        26           okhgg          1.5          USD            1.39         EUR
    6. 03.11.2017    15:43:00      Max Muster    DEF-456        27           oppojkj        3            EUR            3            EUR
    Sergei gefällt das.