[gelöst] [Programmierung] Parser-Frage

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

[gelöst] [Programmierung] Parser-Frage

Beitrag von heisenberg » 07.01.2021 15:21:18

Hallo,

ich habe gerade eine Parseraufgabe hier, die eigentlich einfach ist, wo ich keinen Eleganten Weg sehe. Das ist auch so eine Aufgabe, die öfters einmal auftaucht.

Ich habe so eine Zeile vom asterisk in der Master.csv:

Code: Alles auswählen

"","+4912349668222","+491234668249","from-provider-customer","""callername"" <+4912345668222>","SIP/provider-customer-00000006","SIP/provider-customer-context-00000007","Dial","SIP/01234567@provider-customer-context,1500,tT","2021-01-07 13:59:56","2021-01-07 14:00:14","2021-01-07 14:00:35",38,20,"ANSWERED","DOCUMENTATION","1610024396.8",""
Die Feldaufteilung ist so:

Code: Alles auswählen

#
#        1 - ?
#        2 - Absender
#        3 - Ziel
#        4 - Context
#        5 - CallerID (name+num)
#        6 - Channel-1
#        7 - Channel-2
#        8 - Application
#        9 - Application Args
#       10 - Call Date/Time Start
#       11 - Call Date/Time End
#       12 - Call Result
#       13 - Log Type 
#       14 - Log Time Stamp
#
Der (Perl-) Regex der den Zeilenaufbau beschreibt ist ziehmlich einfach:

Code: Alles auswählen

^("(.*)")(,("(.*)"))+$
D. h. da ist immer irgendwas von Anführungszeichen umschlossen und als Feldtrenner ist das Komma. Problem dabei ist nur: Das Komma kann natürlich auch sonst überall in den Werten auftauchen. D. h. ich kann da keinen split mit Komma durchführen. Auch Anführungszeichen werden in den Feldinhalten exzessiv benutzt. Mit dem beschriebenen Regex bekomme ich jetzt auch keine Einzelfelder raus, weil das bei Wiederholungen nicht angeboten wird. Ich möchte da auch nicht händisch einen fetten Regex machen, der die 14 Felder nacheinander aufführt, weil das schlecht wartbar ist.

Gedanke war eine Schleife mit einer wiederholten Regex Prüfung durchzuführen, der mir dann jeweils das nächste Feld liefert.

Habt Ihr eine Idee dazu, wie man so etwas elegant löst?

(Die Formulierung der Frage hat schon ein bisschen geholfen. Vielleicht komme ich selbst auf die Lösung...)
Zuletzt geändert von heisenberg am 08.01.2021 16:48:56, insgesamt 2-mal geändert.

DeletedUserReAsG

Re: [Programmierung] Parsing-Frage

Beitrag von DeletedUserReAsG » 07.01.2021 16:04:22

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 15:21:18
da ist immer irgendwas von Anführungszeichen umschlossen
Optimale Voraussetzungen, das in ein Array oder eine Liste oder was auch immer zu stecken, und von da aus weiterzuverarbeiten. Oder muss es auf Biegen und Brechen was mit grep und in einer Zeile sein?

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

Re: [Programmierung] Parsing-Frage

Beitrag von heisenberg » 07.01.2021 16:06:07

Nur eine Zeile ist jetzt gar nicht mal so wichtig, aber was elegantes und nicht unnütz aufwändiges, also als Lerneffekt für die Zukunft. Aktuell versuche ich es mit Perl.

Mit der Brechstange kriege ich das schon auch hin.

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

Re: [Programmierung] Parsing-Frage

Beitrag von tobo » 07.01.2021 16:45:33

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 15:21:18
Der (Perl-) Regex der den Zeilenaufbau beschreibt ist ziehmlich einfach:

Code: Alles auswählen

^("(.*)")(,("(.*)"))+$
D. h. da ist immer irgendwas von Anführungszeichen umschlossen und als Feldtrenner ist das Komma.
Und was ist mit diesen ',38,20,'?

Code: Alles auswählen

,"2021-01-07 14:00:35",38,20,"ANSWERED",
Wenn das nicht klar strukturiert ist, dann bekommst du da unter Umständen ein Logik-Problem!? Sprich, wenn du eine gerade Anzahl von Hochkommas vor einem Komma (und damit zwischen zwei Kommas) hast und dieses Komma aber trotzdem kein Trenner ist.

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

Re: [Programmierung] Parsing-Frage

Beitrag von heisenberg » 07.01.2021 16:57:24

Und was ist mit diesen ',38,20,'?
Danke für den Hinweis. Hatte ich klar übersehen, auch wenn ich mich dunkel erinnere. Also: Ja, bei manchen Werten gibt es keine Einfassung in Anführungszeichen. Sollte aber nicht das Problem sein.

Dann wäre der Feld-Regex halt das(der natürlich nur im gesamten Zeilenzusammenhang so funktioniert):

Code: Alles auswählen

(("(.*)")|(.*))
Das grundsätzliche Problem an der Geschichte ist ja, dass ich mit einem Muster direkt nicht auf ein Feld gehen kann, sondern immer alle Felder im Gesamtzusammenhang sehen muss. Ansonsten endet der Regex mittendrin und die ganze Erkennung ist dann vermurkst.

Nachtrag

Asterisk schreibt auch eine master.db(=sqlite3). Ich denke ich werde das damit machen, bin aber trotzdem interessiert zu schauen, wie man das geschickt lösen kann.

Nachtrag-2

Ich finde das Logfile-Format ja schon (immer) etwas bescheiden. Das wird eigentlich nur noch vom oracle db log übertroffen.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parsing-Frage

Beitrag von Meillo » 07.01.2021 18:03:37

Da laut der Regexp Anfuehrungsstriche Pflicht sind, kannst du in awk so die Zeile in deine 14 Felder unterteilen, die dir dann als $1 bist $14 zur Verfuegung stehen:

Code: Alles auswählen

awk -F '","' '
{ sub(/^"/,"",$0); sub(/"$/,"",$0); }
...
'
Vielleicht hilft es dir ja weiter.


Edit: Wobei tobo schon gezeigt hat, das die Regexp gar nicht zutrifft. :facepalm: ... also kannst du letztlich nur raten oder versuchen mit einer Statemachine zu parsen ... was aber am Ende auch auf Raten rauslaufen wird, und zwar immer dann wenn du auf mehr als 14 Felder kommst. Letztlich muss man wohl einsehen, dass das Logformat write-only ist. :roll: :lol:
Use ed once in a while!

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

Re: [Programmierung] Parsing-Frage

Beitrag von heisenberg » 07.01.2021 18:06:52

Meillo hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 18:03:37
Da laut der Regexp Anfuehrungsstriche Pflicht sind ...
Wie ich nachträglich festgestellt habe: Sind sie nicht!
Zuletzt geändert von heisenberg am 07.01.2021 18:08:17, insgesamt 1-mal geändert.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parsing-Frage

Beitrag von Meillo » 07.01.2021 18:07:22

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 18:06:52
Meillo hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 18:03:37
Da laut der Regexp Anfuehrungsstriche Pflicht sind ...
Wie ich nachträglich festegestellt habe: Sind sie nicht!
Sorry, siehe Edit oben.
Use ed once in a while!

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

Re: [Programmierung] Parsing-Frage

Beitrag von heisenberg » 07.01.2021 18:13:30

Es freut mich ja, zu sehen, dass ich nicht komplett doof bin und das auch für Andere kein all zu leichtes Unterfangen zu sein scheint. Aber wenn das Dingens so einfach mit einem Regex zu fassen ist, dann muss das auch lösbar sein.

Im Endeffekt frage ich mich allerdings, ob da nicht noch mehr Pferdefüsse kommen, wenn ich mal anfange die echten Daten zu parsen. Aber so vom Entwerfen der Datenstruktur ist es ja eigentlich ziehmlich einfach:
  1. Ich haue alle die Werte kommagetrennt in die Datei
  2. Manche Werte fasse ich in " ein manche nicht.(Int? String? ...?)
  3. Es ist mir egal was in den Feldern drin steht
Also eigentlich keine großen Überraschungen. Das Linux-Audit-Log weisst ja auch solche Züge auf.

In der Zwischenzeit freue ich mich, dass ich mein Problem mit einem simplen SQL-Statement gelöst habe.

Aber so ganz allgemein, mag ich das nicht:
Meillo hat geschrieben:

Code: Alles auswählen

awk -F '","' '
{ sub(/^"/,"",$0); sub(/"$/,"",$0); }
...
'
So als persönlicher Grundsatz: Ich möchte nicht Daten verändern müssen, wenn auch nur im Speicher, um Inhalte auszulesen. Ich befürchte, dass ich vielleicht einmal eine Änderung vornehme und dann mit veränderten Daten hantiere, statt mit den Originaldaten.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parsing-Frage

Beitrag von Meillo » 07.01.2021 18:56:54

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 18:13:30
Im Endeffekt frage ich mich allerdings, ob da nicht noch mehr Pferdefüsse kommen, wenn ich mal anfange die echten Daten zu parsen. Aber so vom Entwerfen der Datenstruktur ist es ja eigentlich ziehmlich einfach:
  1. Ich haue alle die Werte kommagetrennt in die Datei
  2. Manche Werte fasse ich in " ein manche nicht.(Int? String? ...?)
  3. Es ist mir egal was in den Feldern drin steht
Jedes Datenformat, das mehrere Werte in einen String packt, braucht einen Escaping-Mechanismus, da damit gerechnet werden muss, dass der Separator auch in einem Wert vorkommt. Folglich, darf man Feldinhalte nicht 1:1 ausgeben, sondern muss sie durch eine escape-Funktion laufen lassen, damit in der Ausgabe alle Problemzeichen in den Feldinhalten escapet sind.

Es geht immer schief, wenn man z.B. so XML schreibt:

Code: Alles auswählen

printf("<foo>%s</foo>", bar);
Ebenso geht es immer schief, wenn man so CSV schreibt:

Code: Alles auswählen

printf("%s;%s;%s\n", foo, bar, baz);
Darum gibt es XML- und CSV-Libaries, die das Escaping automatisch fuer einen machen.

Das Schreiben ist kein Problem, aber beim Lesen entstehen Mehrdeutigkeiten. Darum write-only.


Aber so ganz allgemein, mag ich das nicht:
Meillo hat geschrieben:

Code: Alles auswählen

awk -F '","' '
{ sub(/^"/,"",$0); sub(/"$/,"",$0); }
...
'
So als persönlicher Grundsatz: Ich möchte nicht Daten verändern müssen, wenn auch nur im Speicher, um Inhalte auszulesen. Ich befürchte, dass ich vielleicht einmal eine Änderung vornehme und dann mit veränderten Daten hantiere, statt mit den Originaldaten.
Ich sehe dieses Codestueck als eine Art CSV-Library fuer eine triviale Form von Logformat (wie in der geposteten Regexp beschrieben) an.

Der Code entfernt lediglich die Separator, aendert an den Werten nichts.

Eine richtige XML-/CSV-Library muss allerdings auch die Werte aendern! Denn sie muss &lt; zu < umwandeln bzw. "" im Feldinhalt zu ".

Dein Grundsatz ist nur haltbar, wenn du jeden Einzelwert in eine separate Datei speicherst, weil es da keine Separators gibt. (Das ist der qmail-Ansatz, um Parsing zu vermeiden.) Sobald du mehrere beliebige Werte zu einem String vereinen willst (dessen Alphabet nicht groesser ist als das der Werte), dann kommst du um Escaping nicht rum und folglich musst du beim Schreiben und beim Lesen die Feldinhalte aendern koennen. ;-)

(Nun gut, du kannst fertige Bibliotheken nehmen, statt es selbst zu tun. Stell dir einfach vor, obiger Code waere eine fertige Bibliothek von mir, die du verwendest. :-P )
Use ed once in a while!

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 07.01.2021 19:48:35

Ich will das aktuell nicht durchkämpfen, meine aber, dass das parsing ist trotzdem möglich ist. Ob's sinnvoll ist, ist dann gleich eine andere Frage(wie z. B. auch mehrfach unterbrochene Subnetzmasken: Möglich: Ja! Aber was muss man verbrochen haben, um so eine Strafe zu verdienen?). Evtl. komme ich da nochmal drauf zurück.

Ich erinnere mich wie gesagt noch an das Linux Audit Log. Da ist das so ähnlich, wenn auch nicht ganz so krass. Ich habe da auch mal eine kleine Parserspezifikation(für Menschen) geschrieben. Da taucht das auf jeden Fall auf, dass in einem Feld, das normalerweise so verwendet wird...

Code: Alles auswählen

name="/usr/bin/tr"
dann auch noch mal eine ganze Datenstruktur drin steht, z. B.:

Code: Alles auswählen

msg='PAM: setcred acct="root" : exe="/usr/sbin/sshd" (hostname=censored, addr=censored terminal=ssh res=success)'
Der Fall ist ja unproblematisch, da die Quotings unterschiedlich sind. Ein Fallback von audit ist auch noch, dass er ggf. auf base64 als Variablendatenformat umschaltet, falls da aussergewöhnliche Zeichen enthalten sind. Aber da müsste ich nochmal genauer nachschauen, ob da nicht vielleicht doch auch so etwas kauziges drin war, wie jetzt in dem asterisk-Log.
Zuletzt geändert von heisenberg am 07.01.2021 19:50:10, insgesamt 1-mal geändert.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parser-Frage

Beitrag von Meillo » 07.01.2021 20:17:15

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 19:48:35

Code: Alles auswählen

msg='PAM: setcred acct="root" : exe="/usr/sbin/sshd" (hostname=censored, addr=censored terminal=ssh res=success)'
Der Fall ist ja unproblematisch, da die Quotings unterschiedlich sind. Ein Fallback von audit ist auch noch, dass er ggf. auf base64 als Variablendatenformat umschaltet, falls da aussergewöhnliche Zeichen enthalten sind.
Beide Verfahren (anderes Quoting, bzw. base64) reduzieren das Alphabet des Wertestrings. Im ersten Fall darf kein Singlequote im Wert vorhanden sein -- das waere wie wenn in den Asterisk-Werten keine Kommas sein duerfen --, im zweiten Fall wird der beliebige String mittels Base64 so kodiert (auch eine Form von Escaping), damit er nur noch Zeichen eines begrenzten Alphabets enthaelt. In beiden Faellen muss man beim Schreiben und beim Lesen kodieren/escapen.

Das sind gute Beispiele, die meine Aussagen untermauern.
Use ed once in a while!

Benutzeravatar
bluestar
Beiträge: 2419
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: [Programmierung] Parser-Frage

Beitrag von bluestar » 07.01.2021 21:04:53

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 15:21:18
Hallo,

ich habe gerade eine Parseraufgabe hier, die eigentlich einfach ist, wo ich keinen Eleganten Weg sehe. Das ist auch so eine Aufgabe, die öfters einmal auftaucht.

Ich habe so eine Zeile vom asterisk in der Master.csv
Lass doch Asterisk die CDR Daten direkt in eine MariaDB, PostgrSQL oder Sqlite laufen, das ist doch deutlich komfortabler, zumal du direkt mit SQL darauf zugreifen kannst.

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 07.01.2021 21:37:52

bluestar hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 21:04:53
Lass doch Asterisk die CDR Daten direkt in eine MariaDB, PostgrSQL oder Sqlite laufen, das ist doch deutlich komfortabler, zumal du direkt mit SQL darauf zugreifen kannst.
Wie schon in der PN geschrieben es steht weiter oben bereits.

Ich habe aber auch gemerkt, dass in der SQLite-Datei deutlich weniger Informationen drin stehen als in der CSV. Muss ich mir nochmal das Logging anschauen, ob ich da evtl. was hochdrehen muss.

Benutzeravatar
bluestar
Beiträge: 2419
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: [Programmierung] Parser-Frage

Beitrag von bluestar » 07.01.2021 23:34:54

heisenberg hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 21:37:52
bluestar hat geschrieben: ↑ zum Beitrag ↑
07.01.2021 21:04:53
Lass doch Asterisk die CDR Daten direkt in eine MariaDB, PostgrSQL oder Sqlite laufen, das ist doch deutlich komfortabler, zumal du direkt mit SQL darauf zugreifen kannst.
Ich habe aber auch gemerkt, dass in der SQLite-Datei deutlich weniger Informationen drin stehen als in der CSV. Muss ich mir nochmal das Logging anschauen, ob ich da evtl. was hochdrehen muss.
Du kannst bei Asterisk die Daten, die in der CDR Datenbank geloggt werden relativ genau steuern, schau dir mal in /etc/asterisk/cdr_sqlite3_custom.conf an.

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 07.01.2021 23:36:13

Das was mir fehlt sind nicht die Datenfelder, sondern die Datensätze. Aber kann gut sein, dass ich da cdr.log und asterisk.log durcheinanderwerfe.

Ich sehe denn aktuell einen Anruf, mit der Anrufernummer und der Zielrufnummer(Handy), wenn er z. B. darauf weitergeleitet wurde. Aber ich sehe nicht die Telefonnnummer, die der Anrufer gewählt hat(also die Komplettnummer mit Durchwahl bevor weitergeleitet wurde). Aber ich kann ja nochmal prüfen, ob die Info in einem verfügbaren Datenfeld vielleicht möglich ist.

Danke!

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: [Programmierung] Parser-Frage

Beitrag von Lord_Carlos » 08.01.2021 09:14:09

Ist die erste Zeile einer CSV Datei nicht angegeben welche Felder vorhanden sind?
Koennte das nicht nuetzlich sein beim Parsen. Dann weist du immer genau wie viele Felder vorhanden sind.

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parser-Frage

Beitrag von Meillo » 08.01.2021 10:28:53

Ich habe mir die Daten nochmal genauer angeschaut:

Code: Alles auswählen

"","+4912349668222","+491234668249","from-provider-customer","""callername"" <+4912345668222>","SIP/provider-customer-00000006","SIP/provider-customer-context-00000007","Dial","SIP/01234567@provider-customer-context,1500,tT","2021-01-07 13:59:56","2021-01-07 14:00:14","2021-01-07 14:00:35",38,20,"ANSWERED","DOCUMENTATION","1610024396. 8",""
Die Regexp passt zwar nicht, aber das Format scheint doch eindeutig zu sein:

- Separator ist das Komma
- Felder sind entweder in Doublequotes eingeschlossen oder nicht
- Wenn Felder in Doublequotes eingeschlossen sind, dann sind die literalen Doublequotes im Feldinhalt verdoppelt

Das ist Standard-CSV und sollte von CSV-Libraries korrekt geparst werden koennen.

(Gestern Abend hatte ich das Escapen (= Verdoppeln) der Doublequotes uebersehen.)


Weil ich gerade Lust darauf hatte, habe ich den Ansatz mal kurz in C runtergeschrieben:
pastebin/?mode=view&s=41238
... nur zur Illustration. Da stecken sicherlich noch Fehler in Randfaellen drin. Meine Lust ist geschwunden, als obige Zeile aus dem Logfile korrekt geparst worden ist. Darum ist es mir letztlich nur gegangen.

Code: Alles auswählen

B-) ./csv-parse <in
Field 01: 
Field 02: +4912349668222
Field 03: +491234668249
Field 04: from-provider-customer
Field 05: "callername" <+4912345668222>
Field 06: SIP/provider-customer-00000006
Field 07: SIP/provider-customer-context-00000007
Field 08: Dial
Field 09: SIP/01234567@provider-customer-context,1500,tT
Field 10: 2021-01-07 13:59:56
Field 11: 2021-01-07 14:00:14
Field 12: 2021-01-07 14:00:35
Field 13: 38
Field 14: 20
Field 15: ANSWERED
Field 16: DOCUMENTATION
Field 17: 1610024396. 8
Field 18: 
Use ed once in a while!

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 08.01.2021 10:46:01

(Gestern Abend hatte ich das Escapen (= Verdoppeln) der Doublequotes uebersehen.)
Du hast tatsächlich recht. Ich habe da offensichtlich nicht so genau hingeschaut und mir eine andere Erklärung zurechtgebogen. Aber das was Du sagst, ist absolut korrekt. Es ist tatsächlich eine Verdoppelung.

Und nur nochmal vollständigkeitshalber. Ich hatte den Regex für das Feld korrigiert. D. h. ein richtiger Regex war zwar nicht in Gänze aufgeführt, ist aber problemlos möglich.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parser-Frage

Beitrag von Meillo » 08.01.2021 11:04:18

heisenberg hat geschrieben: ↑ zum Beitrag ↑
08.01.2021 10:46:01
D. h. ein richtiger Regex war zwar nicht in Gänze aufgeführt, ist aber problemlos möglich.
Tut mir leid, hier nochmal pedantisch sein zu muessen: Ich denke, dass eine Regexp nicht moeglich ist, weil das Parsingproblem zu komplex ist, um es mit einer regulaeren Grammatik ausdruecken zu koennen. Der Escaping-Mechanismus erfordert State und eine regulaere Sprache hat keinen State. (Die theoretischen Informatiker hier koennen das sicher genauer ausfuehren.)


Beispiel:

Code: Alles auswählen

"foo","bar"",""baz","quux"
Das sind drei Felder, die man mittels Regexp nicht erkennen kann, weil man sie nicht von diesen Records mit jeweils vier Feldern unterscheiden kann:

Code: Alles auswählen

"foo","bar","baz","quux"
"foo","bar""","""baz","quux"
Use ed once in a while!

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 08.01.2021 11:40:48

Meillo hat geschrieben: ↑ zum Beitrag ↑
08.01.2021 11:04:18
Tut mir leid, hier nochmal pedantisch sein zu muessen: Ich denke, dass eine Regexp nicht moeglich ist, weil das Parsingproblem zu komplex ist, um es mit einer regulaeren Grammatik ausdruecken zu koennen. Der Escaping-Mechanismus erfordert State und eine regulaere Sprache hat keinen State. (Die theoretischen Informatiker hier koennen das sicher genauer ausfuehren.)
Ich bin anderer Meinung. Ich denke der Escaping-Mechanismus ist vollkommen irrelevant für einen Regex-Ausdruck, der auf eine Zeile matchen kann oder nicht.

Erklärung

Das ist der Ausdruck für ein Feld:

Code: Alles auswählen

(("(.*)")|(.*))
Ich nenne das jetzt mal f.

Dann wäre der Zeilen Regex-Ausdruck(für eine Zeile mit dynamischer Feldanzahl):

Code: Alles auswählen

^f(,f)+$
Oder komplett ausgeführt:

Code: Alles auswählen

^(("(.*)")|(.*))(,(("(.*)")|(.*)))+$
Der Knackpunkt ist jetzt die Verankerung am Feldanfang und am Feldende. Natürlich können zwischendrin Kommata und Doppelanführungszeichen den Regex theoretisch stören. Der Punkt ist aber, dass sich die Zeichenkette von den Enden her korrekt aufbauen muss und daraus ergibt sich die Eindeutigkeit auch bei egal welchen Inhalten, sofern die bisher festgestellten Konventionen eingehalten werden.

Ich kann da jetzt keine theoretisch klar nachvollziehbare Erklärung für bieten. Es ist im Moment reine Intuition. Ich würde Dich bitten, dass jetzt mal so - unbewiesen und demnach faktisch falsch - stehen zu lassen. (Dass das z. B. bei XML ganz anders ist, das mag so sein.. Auch ist mein Verständnis, wie Regexes-Prozessing tatsächlich durchgeführt wird eher gering. D. h. dass könnte mir auch nochmal vor die Füsse fallen.)

Nächster möglicher Schritt wäre ein Programm, um mit Zufallsdaten diese Vermutung zu testen, dass es tatsächlich so ist. Falls ich damit Erfolg habe, würde ich mich nochmal melden.

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 08.01.2021 11:54:14

Hier ist mal ein erster erfolgreicher Test mit einem Testdatenbeispiel von Dir.

Für die tatsächliche Erfassung der Feldinhalte kann ich nur mit fester Feldanzahl arbeiten, da ich auf die Captures bei Wiederholungen mit Perl(und von keiner anderen Sprache, die ich kenne und zumindest ansatzweise nutzen kann. Haskell wäre für so ein Problem vielleicht nochmal interessant) nicht zugreifen kann.

Code: Alles auswählen

#!/usr/bin/perl

$_='"foo","bar""","""baz","quux"';

print "data: $_\n\n";

$f='(("(.*)")|(.*))';

# Muster mit dynamischer Feldanzahl
$re="^$f(,$f)\$";

/$re/;
if($1) { print "regex matched!\n\n"; }

# Wiederholung mit Muster mit fester Feldanzahl, um an die Werte zu kommen
$re_expanded='^(("(?<a1>.*)")|(?<b1>.*)),(("(?<a2>.*)")|(?<b2>.*)),(("(?<a3>.*)")|(?<b3>.*)),(("(?<a4>.*)")|(?<b4>.*))$';

/$re_expanded/;

print("a1: ".$+{a1}."\n");
print("b1: ".$+{b1}."\n");

print("a2: ".$+{a2}."\n");
print("b2: ".$+{b2}."\n");

print("a3: ".$+{a3}."\n");
print("b3: ".$+{b3}."\n");

print("a4: ".$+{a4}."\n");
print("b4: ".$+{b4}."\n");
Ausgabe:

Code: Alles auswählen


data: "foo","bar""","""baz","quux"

regex matched!

a1: foo
b1: 
a2: bar""
b2: 
a3: ""baz
b3: 
a4: quux
b4: 

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: [Programmierung] Parser-Frage

Beitrag von Meillo » 08.01.2021 12:24:30

Test mal folgende Inputzeile, die ebenfalls vier Felder hat:

Code: Alles auswählen

"foo","bar""","""baz"",""quux","blub"
Korrekte Ausgabe waere:

Feld 1: foo
Feld 2: bar"
Feld 3: "baz","quux
Feld 4: blub


Dein Testcode erzeugt:

Code: Alles auswählen

data: "foo","bar""","""baz"",""quux","blub"

regex matched!

a1: foo","bar""
b1: 
a2: ""baz"
b2: 
a3: "quux
b3: 
a4: blub
b4: 
Das vorhandene oder fehlende Escapen der Doublequotes innerhalb der Feldinhalte koennen wir vernachlaessigen, es geht mir nur um die korrekte Identifikation der Separatoren und damit um die korrekte Feldbildung.


Ich kann das schon so stehen lassen, wenn du es selber rausfinden willst. Ich tue mir nur schwer damit, falsche Aussagen stehen zu lassen. Ich muss nicht erklaeren. Ich kann auch einfach nur Gegenbeispiele liefern und Testdaten, die deine Aussagen widerlegen. Wie man das in der wisschenschaftlichen Herangehensweise halt so macht.

Von mir aus kann das auch gerne jemand anderes tun. Vielleicht sind meine Formulierungen gerade einfach nicht so gluecklich ...

Am liebsten waere es mir, wenn ein theoretischer Informatiker hier sachlich darlegen koennte, warum sich das Problem mittels Regexps nicht loesen laesst. Mein Wissen in Sprachentheorie ist dafuer zu begrenzt und ich kann eben auch nicht mit der Chomsky-Hierarchie argumentieren, weil ich sie nur ungefaehr kenne und die Sprachklassen nicht wirklich erklaeren kann.

Gerne lasse ich mich in meiner Meinung auch widerlegen!


Oder wuenschst du dir einfach mehr Zeit, um selber daran zu arbeiten? Und ich soll nicht so schnell posten?
Use ed once in a while!

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 08.01.2021 13:02:33

Das läuft gerade schon so, wie ich das gut haben kann: Ich habe eine Idee und sie wird entweder widerlegt oder nicht. Und das Spielchen geht so lange weiter, bis einer keine Lust mehr hat, oder die Erkenntnis steht: Ja, das geht definitiv (nicht)!

Wenn ein theoretischer Informatiker da wäre, ist die Frage ob ich den überhaupt verstehen würde!

Ansonsten: Mein Beispielcode ist auf eine fixe Feldlänge von 4 Feldern festgelegt. Mit 5 Feldern funktioniert der nicht(Anpassung notwendig)! Anpassung auf variable Feldlänge ist möglich, da müsste ich mir den erweiterten Regex allerdings dynamisch zusammenbauen.

Nachtrag

Interessant auch ist die Art des falschen Ergebnisses: Das Ergebnis fasst Feld 1 und Feld 2 zusammen, da es ja nur 4 Felder geben darf und der Feldtrenner zwischen Feld 1 und Feld 2 wird logischerweise als Inhalt gewertet. Der Regex ist logisch nachvollziehbar passend. Folge: Wenn z. B. Feld 1 und Feld n (=letztes Feld) mit Anführungszeichen umschlossen sind, dann könnte die dynamische Erkennung daraus auch ein einziges valides Feld machen(ist ja von Doppelanführungszeichen umschlossen). Erst die Information wie viele Felder im Datensatz vorhanden sind, findet (vermutlich) aus der Lösungsmenge die korrekte Einzellösung heraus.


Nachtrag-2

Ich denke Du hast Recht. An Deinem Beispiel sehe ich, selbst, wenn ich es schon manuell als Mensch mir anschaue, kann ich keine eindeutige, richtige Lösung bestimmen. Anhand des Musters kann es mehrere zutreffende Lösungen geben, die alle derart sind, das einzelne Felder zusammengefasst werden (1+2,2+3,3+4,4+5) und dadurch die Feldzahl auf 4 reduziert wird. Es ist schlicht nicht bestimmbar was die eine richtige Lösung ist. Eine einzig richte Lösung im Kopf algorithmisch zu erreichen, ist aber der erste Schritt, ohne den es keinen 2. geben kann.

Und wahrscheinlich wird das nicht auch der einzige Fehlerfall bleiben. Wenn die Inhalte 1000 mögliche Felder sein könnten, dann gibt es halt wesentlich mehr Falschmöglichkeiten.

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

Re: [Programmierung] Parser-Frage

Beitrag von heisenberg » 08.01.2021 14:34:01

Zurück zum eigentlichen Problem:

Das war es was mir fehlte:
  1. externer Anrufer ruft auf einer Festnetzrufnummer der Firma an
  2. Nummer klingelt intern
  3. Nummer wird intern nicht abgehoben
  4. Nummer wird ggf. auf das Handy vermittelt.
  5. Im Log fehlte die ursprünglich angenommene Festnetzrufnummer
Ich habe jetzt im Asterisk Wahlplan eines der Abrechungsfelder(CDR(userfield)) mit der der angerufenen Festnetzrufnummer belegt. Damit habe ich die Information.

Antworten