Strings zerlegen / komplex

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
rannseier
Beiträge: 79
Registriert: 24.09.2007 12:37:30

Strings zerlegen / komplex

Beitrag von rannseier » 20.09.2024 17:10:33

Hallo zusammen,

Ich habe hier ein kleines Problem. Ich möchte Strings auslesen und zerlegen. Die Strings liegen Zeilenweise in einer Datei vor.

Auslesen geht ja relativ einfach:

Code: Alles auswählen

FILE=$1
while read LINE
done <$FILE
In dem String sind hintereinander einzelne Bereiche gespeichert, beginnend mit einem Kennbuchstaben, gefolgt von der Länge in Hex und dem eigentlich Payload.

Beispiel:
A00A 123456789

In einer Zeile stehen mehrere Kennbuchstaben hintereinander:
A011 1234567890123456 B012 12345678901234567

Das kann ich ja einfach so zerlegen:

Code: Alles auswählen

#!/bin/bash

datum_scriptstart=$(date +%Y-%m-%d--%H-%M-%S)

FILE=$1

tickets_num=0

while read LINE

do
    let "tickets_num=$tickets_num+1"

    ticket_a_indicator=${LINE:0:1}
    ticket_a_length_hex=${LINE:1:3}
    ticket_a_length_dec=$(echo $((16#$ticket_a_length_hex)))
    ticket_a=${LINE:0:$ticket_a_length_dec+5}

    LINE=${LINE:ticket_a_length_dec+5}

    ticket_b_indicator=${LINE:0:1}
    ticket_b_length_hex=${LINE:1:3}
    ticket_b_length_dec=$(echo $((16#$ticket_b_length_hex)))
    ticket_b=${LINE:0:$ticket_b_length_dec+5} 

    echo Ticket A:.$ticket_a.
    echo Ticket B:.$ticket_b.

done <$FILE

datum_scriptende=$(date +%Y-%m-%d--%H-%M-%S)

echo "Scriptstart:                                " $datum_scriptstart
echo "Scriptende:                                 " $datum_scriptende
                
exit 0

Nur leider ist die Reihenfolge nicht fest, auch nicht jeder Eintrag muss vorkommen. Dafür können manche Einträge mehrmals vorkommen (max 2 mal). Die Felder selbst sind fest (z.B. A, B und C wobei C doppelt vorkommen kann.

Das wäre genau so richtig als Eingabe:
B012 12345678901234567 A011 1234567890123456 C012 12345678901234567
wie auch:
B012 12345678901234567 C012 12345678901234567 A011 1234567890123456 C010 123456789012345


Mir schwebt da irgendwie eine weitere while-Schleife vor habe aber keinen Plan, wie ich das umsetzen soll. Letztendlich will ich A, B und C1+C2 in einzelnen Variablen haben und von dort weiter verarbeiten.

Hat jemand eine Idee?

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Strings zerlegen / komplex

Beitrag von heisenberg » 20.09.2024 17:48:37

Ich wiederhole nochmal:

Du hast eine Datenstruktur, die besteht aus:
  • Einer Datei
  • Eine unbestimmte Anzahl von Zeilen in dieser Datei
  • Ein oder mehrere gleichartige Datenelemente pro Zeile
  • Ein Datenelement ist gekennzeichnet durch:
    • Einen Kopfteil und einem Datenteil
    • Kopfteil und Datenteil sind durch Leerzeichen getrennt
    • Der Kopfteil beginnt mit einem Grossbuchstaben (Kennbuchstabe), gefolgt von der Länge des Datenteils als Hex-Wert
    • Der Datenteil (Payload) hat einen beliebigen Inhalt mit der im Kopfteil angegebenen Länge
    • Der gleiche Kennbuchstabe darf in einem beliebigen Kopfteil maximal 2 Mal pro Datei vorkommen.
Meine Frage dazu: Ist in der Anzahl der Datenelemente pro Zeile auch noch eine implizite Information enthalten, oder könnte man auch die eines nach dem anderen unabhängig verarbeiten?

Sprich, ist das folgende ...

Code: Alles auswählen

A05 12345 B09 123456789
das gleiche wie ... ?

Code: Alles auswählen

A05 12345
B09 123456789

rannseier
Beiträge: 79
Registriert: 24.09.2007 12:37:30

Re: Strings zerlegen / komplex

Beitrag von rannseier » 20.09.2024 21:33:30

Die einzelnen Zeilen gehören zu jeweils einem Datensatz.

Es gibt Elemente, die kommen genau ein mal vor (A+B). Das Element C kann 0-3 mal vorkommen, das Element D kann 0 oder 1 mal vorkommen.

michaa7
Beiträge: 4916
Registriert: 12.12.2004 00:46:49
Lizenz eigener Beiträge: MIT Lizenz

Re: Strings zerlegen / komplex

Beitrag von michaa7 » 20.09.2024 21:53:11

Was ich an deiner Problembeschreibung nicht klar beschrieben finde:

1) eine Zeile ist ein Datensatz (mit mehren Bereichen die aus jeweils einem Buchstaben aus A-D+Hex und payload bestehen)
Richtig?

2) C kann pro ***Zeile*** = Datensatz 0/1/2/3 mal vorkommen (nicht nur pro Datei)
Richtig?
gruß

michaa7

-------------------------------
Menschen ändern gelegentlich ihre Ansichten, aber nur selten ihre Motive. (Oskar Negt)

rannseier
Beiträge: 79
Registriert: 24.09.2007 12:37:30

Re: Strings zerlegen / komplex

Beitrag von rannseier » 20.09.2024 22:00:45

1+2 sind richtig.

Noch ein Beispiel:
A05 12345 B09 123456789
B09 123456789 A05 12345
C05 12345 B09 123456789 A05 12345
C05 12345 B09 123456789 A05 12345 C05 12345

juribel
Beiträge: 331
Registriert: 20.06.2023 10:17:01

Re: Strings zerlegen / komplex

Beitrag von juribel » 20.09.2024 23:30:17

1. Was möchtest du denn eigentlich überhaupt als Ausgabe erzeugen?

2. Sind die hexadezimalen Längenangaben überhaupt von Bedeutung, oder anders gefragt, sind die einzelnen Teile (Kennbuchstabe+Länge und Inhalt) immer mit Leerzeichen voneinander getrennt oder können sie auch übergangslos ineinander übergehen?

Für Datei- und Stringverarbeitung (und vieles mehr) würde ich eigentlich eher Perl als Programmiersprache nehmen (das ist genau für sowas gebaut), es sei denn, du willst das Ergebnis wirklich unbedingt aus Wissbegierde aus einem Shellskript herauskitzeln :-)

rannseier
Beiträge: 79
Registriert: 24.09.2007 12:37:30

Re: Strings zerlegen / komplex

Beitrag von rannseier » 20.09.2024 23:52:35

zu 1) Die einzelnen Elemente haben noch weitere Subelemente, die durch Leerzeichen geteilt sind. Diese sollen ausgewertet werden.

zu 2) Die Teile sind durch Leerzeichen getrennt. Die einzelnen Subelemente sind ebenfalls durch Leerzeichen getrennt, weswegen die Länge ausgewertet werden muss.

Beispiel.
<identifier 1Zeichen><Länge in Hex 3Zeichen><Leerzeichen><Subelement1><Leerzeichen><Subelement2><Leerzeichen><Subelement3> Bis zu 30 Subelemente
A00F 4711 4712 4713 B00A 4711 4713

Perl ist nicht meins, ich komme aus der Welt mit dem 6502 und 68000er Assembler. ;) . Geht das in der Bash relativ sauber?

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Strings zerlegen / komplex

Beitrag von heisenberg » 20.09.2024 23:55:22

Würde da juribel mal zustimmen: Würde ich eher mal nicht mit bash angehen, sondern mit einer Scriptsprache. Gehen tut das in Bash. Ist halt vergleichsweise anstrengend.

tobo
Beiträge: 2335
Registriert: 10.12.2008 10:51:41

Re: Strings zerlegen / komplex

Beitrag von tobo » 21.09.2024 00:05:34

Da es dir offensichtlich nicht möglich ist, auf eine einfache Eingabe die erwartete Ausgabe zu zeigen, muss man sogar bei der Fragestellung mutmaßen. Womöglich kommt es dir auf die Sortierung innerhalb einer Zeile an!? 2 Möglichkeiten:

Code: Alles auswählen

#!/bin/bash

FILE=$1
while read -r line; do
    #sed -E 's/([ABCD][A-F0-9]+) ([0-9]+)/\1_\2/g; s/ /\n/g' <<< "$line" | xargs -n1 | sort | xargs | sed 's/_/ /g'
    sed -E 's/ ([^ ]+) */_\1\n/g' <<< "$line" | xargs -n1 | sort | xargs | sed 's/_/ /g'
done <"$FILE"

Code: Alles auswählen

$ ./t.sh f
A05 12345 B09 123456789
A05 12345 B09 123456789
A05 12345 B09 123456789 C05 12345
A05 12345 B09 123456789 C05 12345 C05 12345
$

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Strings zerlegen / komplex

Beitrag von heisenberg » 21.09.2024 00:29:18

Ich habe mal ChatGPT um ein Python3 Programm gebeten. Es mussten nur kleine Korrekturen vorgenommen werden. Sieht so aus, als ob das tut:

Meine Testdaten:

Code: Alles auswählen

A05 12345 B09 123456789
B09 123456789 A05 12345
C05 12345 B09 1234 6789 A05 12345
C05 33333 B09 123456789 A05 12345 C05 1  45
C05 33333 B09 123456789 A05 12345 C05 1 a 5 D07 8888888
Das Programm:

Code: Alles auswählen

#!/usr/bin/python3
import re

def parse_line(line):
    # Regex zur Erkennung des Kopf- und Datenteils
    pattern = r'([A-Z])([0-9A-Fa-f]+)\s(.+)'

    # Speichert die gefundenen Datenelemente
    elements = []

    # Entfernt führende und nachfolgende Leerzeichen
    line = line.strip()

    while line:
        # Suche den Kopfteil des nächsten Datenelements
        match = re.match(pattern, line)
        if not match:
            break

        # Extrahiere den Kopfteil (Kennbuchstabe und hexadezimale Länge)
        typ = match.group(1)  # Kennbuchstabe (A, B, C oder D)
        length_hex = match.group(2)  # Hexadezimale Länge
        length = int(length_hex, 16)  # Umwandeln in dezimal

        # Extrahiere den Datenteil basierend auf der Länge
        data_value = match.group(3)[:length]

        # Speichere das Datenelement
        elements.append((typ, length, data_value))

        # Entferne den verarbeiteten Teil aus der Zeile, um mit dem Rest weiterzumachen
        line = match.group(3)[length:].strip()

    return elements


def process_file(filename):
    with open(filename, 'r') as file:
        for line in file:
            elements = parse_line(line)
            print("Zeile analysiert:")
            for elem in elements:
                print(f"Typ: {elem[0]}, Länge: {elem[1]}, Wert: '{elem[2]}'")
            print()  # Leerzeile für bessere Lesbarkeit zwischen den Zeilen


# Beispiel zur Demonstration:
if __name__ == "__main__":
    # Nenne hier den Dateinamen, der verarbeitet werden soll
    filename = 'daten.txt'
    process_file(filename)
Ausgabe:

Code: Alles auswählen

python3 parse.py
Zeile analysiert:
Typ: A, Länge: 5, Wert: '12345'
Typ: B, Länge: 9, Wert: '123456789'

Zeile analysiert:
Typ: B, Länge: 9, Wert: '123456789'
Typ: A, Länge: 5, Wert: '12345'

Zeile analysiert:
Typ: C, Länge: 5, Wert: '12345'
Typ: B, Länge: 9, Wert: '1234 6789'
Typ: A, Länge: 5, Wert: '12345'

Zeile analysiert:
Typ: C, Länge: 5, Wert: '33333'
Typ: B, Länge: 9, Wert: '123456789'
Typ: A, Länge: 5, Wert: '12345'
Typ: C, Länge: 5, Wert: '1  45'

Zeile analysiert:
Typ: C, Länge: 5, Wert: '33333'
Typ: B, Länge: 9, Wert: '123456789'
Typ: A, Länge: 5, Wert: '12345'
Typ: C, Länge: 5, Wert: '1 a 5'
Typ: D, Länge: 7, Wert: '8888888'
Hier ist der ChatGPT-Link zur Sitzung:
https://chatgpt.com/share/66edf878-e844 ... 7b10dde7b5

Der Code kam fast vollständig funktionsfähig von ChatGPT. Nur aus irgend einem unerfindlichen Grund hat ChatGPT das Längenfeld auf 4 Zeichen fix gesetzt. Ansonsten: Eingängiger Code, schön mit Erklärungen versehen, den ich gut nachvollziehen kann.
Zuletzt geändert von heisenberg am 21.09.2024 12:36:11, insgesamt 5-mal geändert.

Benutzeravatar
GregorS
Beiträge: 3124
Registriert: 05.06.2008 09:36:37
Wohnort: Freiburg
Kontaktdaten:

Re: Strings zerlegen / komplex

Beitrag von GregorS » 21.09.2024 00:29:55

Auch ich glaube, dass man durch den geschickten Einsatz der vielen kleinen Unix-Utilities wie cat/grep/sed/rev uvm. zum Ziel kommen kann. Das ist vielleicht mit mehr Schritten verbunden als in Perl, aber ich glaube nicht, dass sich ein Einarbeiten in Perl deshalb lohnen würde.
Wenn man keine Probleme hat, kann man sich welche machen. ("Großes Lötauge", Medizinmann der M3-Hopi [und sog. Maker])

rannseier
Beiträge: 79
Registriert: 24.09.2007 12:37:30

Re: Strings zerlegen / komplex

Beitrag von rannseier » 21.09.2024 00:34:49

So sieht das übrigens komplett aus:

Code: Alles auswählen

XX$203 T0b0 289431 0 20240822 090802 10 11 12 d45bfa7d 1 0 1 00A 3 1234567890 3 1234567890 0 1 3 9876543321 001 0 3 45678907655443 1 24 B10 C1200 345678-345678 ABC fghj[44hhhh,ggghhhhh] 0 D009 0 611070 G02a 20240822 09080217 ? 0000000072 0000001695 E007 020100 V012 0 20 0 1 0 uuuzzz O03b 17000 129280 120480 808 753 0 0 0 FWD 345.31.666.777:31754 O030 17000 0 0 0 0 0 0 0 REMOTE 875.64.567.567:16892 V013 0 20 0 0 0 uzt_uio Z032 23678-GN-5cb19861-3cb61ad36@gfr.tzujkl.mnbvcxxzzz Z01f 3D48347EA227EAB6@777.555.44.33
Daraus ist zu lesen:

Code: Alles auswählen

T0b0 289431 0 20240822 090802 10 11 12 d45bfa7d 1 0 1 00A 3 1234567890 3 1234567890 0 1 3 9876543321 001 0 3 45678907655443 1 24 B10 C1200 345678-345678 ABC fghj[44hhhh,ggghhhhh] 0 
D009 0 611070 
G02a 20240822 09080217 ? 0000000072 0000001695 
E007 020100 
V012 0 20 0 1 0 uuuzzz 
O03b 17000 129280 120480 808 753 0 0 0 FWD 345.31.666.777:31754 
O030 17000 0 0 0 0 0 0 0 REMOTE 875.64.567.567:16892 
V013 0 20 0 0 0 uzt_uio 
Z032 23678-GN-5cb19861-3cb61ad36@gfr.tzujkl.mnbvcxxzzz Z01f 
Die Reihenfolge vom Ursprünglichen Auftreten aus dem Beispiel hier (U und V) ist wichtig. v sind einzelne Zähler die sich auf O beziehen.

Diese einzelnen Strings zu zerlegen ist einfach, die kann ich einfach mit AWK in Variablen packen.

Mir fehlt nur das zerlegen des Strings durch die nicht definierte Reihenfolge.

Grundlegend hätte ich da schon mal eine erste Idee:

Code: Alles auswählen

FILE=$1
while read LINE
    while LINE-Länge >0
    if erstes Zeichen=T
        Länge Substring auslesen
        string_t=Substring
        LINE kürzen um vorherigen Substring
    elif erstes Zeichen=D
        Länge Substring auslesen
        string_d=Substring
        LINE kürzen um vorherigen Substring
    elif erstes Zeichen=V und string_v1=leer
        Länge Substring auslesen
        string_v1=Substring
        LINE kürzen um vorherigen Substring
    elif erstes Zeichen=V und string_v2=leer
        Länge Substring auslesen
        string_v2=Substring
        LINE kürzen um vorherigen Substring
    done LINE

done <$FILE
Würde das mit der inneren und verschachtelten WHILE-Schleife funktionieren?

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Strings zerlegen / komplex

Beitrag von heisenberg » 21.09.2024 00:42:29

Das obige Python-Beispiel funktioniert für die Datei, nachdem ich die Kennbuchstaben erweitert habe und den von Dir bisher nicht spezifizierten Zeilenheader("XX$203") weggenommen habe:

Code: Alles auswählen

python3 parse.py

Zeile analysiert:
Typ: T, Länge: 176, Wert: '289431 0 20240822 090802 10 11 12 d45bfa7d 1 0 1 00A 3 1234567890 3 1234567890 0 1 3 9876543321 001 0 3 45678907655443 1 24 B10 C1200 345678-345678 ABC fghj[44hhhh,ggghhhhh] 0 '
Typ: D, Länge: 9, Wert: '0 611070 '
Typ: G, Länge: 42, Wert: '20240822 09080217 ? 0000000072 0000001695 '
Typ: E, Länge: 7, Wert: '020100 '
Typ: V, Länge: 18, Wert: '0 20 0 1 0 uuuzzz '
Typ: O, Länge: 59, Wert: '17000 129280 120480 808 753 0 0 0 FWD 345.31.666.777:31754 '
Typ: O, Länge: 48, Wert: '17000 0 0 0 0 0 0 0 REMOTE 875.64.567.567:16892 '
Typ: V, Länge: 19, Wert: '0 20 0 0 0 uzt_uio '
Typ: Z, Länge: 50, Wert: '23678-GN-5cb19861-3cb61ad36@gfr.tzujkl.mnbvcxxzzz '
Typ: Z, Länge: 31, Wert: '3D48347EA227EAB6@777.555.44.33'
Das mit den abschliessenden Leerzeichen könnte man noch beheben.

Nachdem Du jetzt echte Testdaten gezeigt hast, wiederhole ich meine Empfehlung: Lieber nicht bash ... Da gibt's ja anscheinend noch etwas mehr zu parsen. Mit einer Scriptsprache kommt da wesentlich besser verstehbarer und wartbarer Code und auch deutlich performanterer Code raus. Den obigen Python-Code kann ich verstehen, obwohl ich Python selbst nur rudimentär programmieren kann.

Nachtrag

Im Moment gehe ich davon aus, dass die Kenntnis einer Scriptsprache nicht vorhanden ist und dass das deswegen für Dich aktuell keine Option ist, die Du nutzen möchtest. Aber grundsätzlich könnte man sich ja mal eine Scriptsprache aussuchen, die einem zusagt, als gutes Werkzeug für komplexere Probleme und sich dort einarbeiten.

Antworten