RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Du hast Probleme mit Deinem eMail-Programm, Webbrowser oder Textprogramm? Dein Lieblingsprogramm streikt?
Antworten
Benutzeravatar
buntewolke
Beiträge: 120
Registriert: 19.06.2021 17:05:28

RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von buntewolke » 22.09.2022 10:35:23

Hallo,

ich bin dabei, mich in das Thema RegEx reinzufuchsen. Und finde kein Beispiel, in dem gezeigt wird, wie man mit einer RegEx-Standardsyntax Dateien filtert/ findet die bspw. zwei Zeichenfolgen "schwarz" UND "weiss" irgendwo in der Datei enthalten. Es gibt Beispiele, in den mit

Code: Alles auswählen

schwarz|weiss
Treffer mit "schwarz" ODER "weiss" filtert. Gibt es keine Standard-UND Syntax in RegEx, um Filter mit "schwarz" UND "weiss" platzsparend zu bauen? Muss ich jeweils die Ausgabe der Suche nach "schwarz" im nächsten Schritt nochmal mit "weiss" filtern, um das gewünschte Ergebnis zu bekommen?

gruss, buntewolke
bin unterwegs mit
Debian, Version 11 (bullseye)

Benutzeravatar
towo
Beiträge: 4055
Registriert: 27.02.2007 19:49:44
Lizenz eigener Beiträge: GNU Free Documentation License

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von towo » 22.09.2022 10:42:06

Code: Alles auswählen

egrep "schwarz|weiss" $file
?

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 10:43:05

Code: Alles auswählen

schwarz.*weiss|weiss.*schwarz
... allerdings arbeiten die Unix-Tools wie `egrep' nur zeilenweise. Wenn du in der ganzen Datei suchen willst, dann musst du es dort in so einer Weise machen:

Code: Alles auswählen

egrep -q schwarz DATEI && egrep -q weiss DATEI && echo "gefunden in DATEI"

Wenn du die Umsetzung aber mit einer Programmiersprache machst, dann kann man dort meist die RE auch auf einen String, der den ganzen Dateiinhalt enthaelt, anwenden. Wenn du dann den Modifier setzt, damit Punkt auch auf Newlines matcht, dann kannst du die RE von oben verwenden.
Zuletzt geändert von Meillo am 22.09.2022 10:44:25, insgesamt 1-mal geändert.
Grund: Beispiel repariert
Use ed once in a while!

uname
Beiträge: 10848
Registriert: 03.06.2008 09:33:02

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von uname » 22.09.2022 10:44:43

Mit RegEx kenne ich mich wenig aus. Aber ich denke dieser Beitrag beschreibt genau dein Problem und zeigt ein paar Lösungen auf.

Ich würde wohl den Befehl grep doppelt, verbunden mit Pipe (|), verwenden. Das ist wohl wirklich schlecht. ;-)
Den genauen Unterschied zu egrep kenne ich auch nicht. Aber ich denke bei einfachen Begriffen ist es egal.

Code: Alles auswählen

grep schwarz dateiname|grep weiss

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von tobo » 22.09.2022 12:10:14

Die Crux ist natürlich, dass die Übereinstimmung in der Datei und nicht in der Zeile abgefragt wird. Eine Lösung mit einem grep wäre z.B.:

Code: Alles auswählen

grep -Pz '(?s)(?=.*weiss)(?=.*schwarz)'
bzw. halt (von oben):

Code: Alles auswählen

grep -Pz '(?s)schwarz.*weiss|weiss.*schwarz'

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 12:55:57

tobo hat geschrieben: ↑ zum Beitrag ↑
22.09.2022 12:10:14
Die Crux ist natürlich, dass die Übereinstimmung in der Datei und nicht in der Zeile abgefragt wird. Eine Lösung mit einem grep wäre z.B.:

Code: Alles auswählen

grep -Pz '(?s)(?=.*weiss)(?=.*schwarz)'
Das ist ein Interessanter Ansatz.

Das ist was ich verstehe:

- Es werden PCRE genutzt (-P)
- (?s) aktiviert die Option dotall, mit der der Punkt auch Newlines matcht
- dann folgen zwei Lookaheads, die das jeweilige Wort irgendwo im Rest des Textes matchen

Falls die Datei die beiden Worte enthaelt, dann matcht letztlich die Stelle vor dem ersten Zeichen. (-o wuerde einen leeren String ausgeben.)

Was ich noch nicht verstehe, ist die Kommandozeilenoption -z. Was ist deren Sinn hier? Sorgt das dafuer, dass der ganze Inhalt der Datei in eine ``Zeile'' gelesen wird?
Use ed once in a while!

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von tobo » 22.09.2022 13:24:39

Genau, (?s) sorgt dafür, dass der . alles matcht (PCRE_DOTALL).
Zum Optionsschalter -z:
man -Lde grep hat geschrieben: -z, --null-data
Behandelt Ein- und Ausgabedaten als Folge von Zeilen, die jede mit einem Null-Byte (das Zeichen ASCII NUL) anstelle eines Zeilenvorschubs endet.[...]
Ohne diese Option könnte das PCRE_DOTALL zwar alles matchen, es würde aber trotzdem nur jeweils eine Zeile sehen.

PS:
Alternativ könnte man auch pcregrep benutzen. Das hat nicht nur Stärken bei der Ausgabe von capturing groups:

Code: Alles auswählen

pcregrep -M '(?s)schwarz.*weiss|weiss.*schwarz'

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 14:13:04

:THX:
Use ed once in a while!

Benutzeravatar
buntewolke
Beiträge: 120
Registriert: 19.06.2021 17:05:28

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von buntewolke » 22.09.2022 15:41:08

DANKE! Da habe ich genug zu lernen! :THX: :)
bin unterwegs mit
Debian, Version 11 (bullseye)

uname
Beiträge: 10848
Registriert: 03.06.2008 09:33:02

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von uname » 22.09.2022 15:43:42

Weiß zufälligerweise jemand ob das auch mit awk geht? Oder ist ja kein Problem aber Und?

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 15:56:01

uname hat geschrieben: ↑ zum Beitrag ↑
22.09.2022 15:43:42
Weiß zufälligerweise jemand ob das auch mit awk geht? Oder ist ja kein Problem aber Und?
Es scheint zu funktionieren:

Code: Alles auswählen

awk 'BEGIN{RS=/.^/} /schwarz.*weiss|weiss.*schwarz/{ print "match" }'
Der Trick ist, den Record-Separator RS auf eine RE zu setzen, die nie matchen kann. Damit wird der ganze Input ein einziger Record.

Ich verstehe nur noch nicht, warum hier der Punkt dann Newlines matcht. Das ist untypisch fuer Unix, aber da es eh eine GNU Extension ist, ist das wohl egal.
Use ed once in a while!

Huo
Beiträge: 412
Registriert: 26.11.2017 14:03:31

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Huo » 22.09.2022 19:58:16

Meillo hat geschrieben: ↑ zum Beitrag ↑
22.09.2022 15:56:01

Code: Alles auswählen

awk 'BEGIN{RS=/.^/} /schwarz.*weiss|weiss.*schwarz/{ print "match" }'
Der Trick ist, den Record-Separator RS auf eine RE zu setzen, die nie matchen kann. Damit wird der ganze Input ein einziger Record.
Kann man in awk den Input-Record-Separator RS wirklich mittels in Slashes eingeschlossenem Suchmuster definieren?

Warum erhalte ich

Code: Alles auswählen

$ awk 'BEGIN{RS=/sep/} /schwarz.*weiss|weiss.*schwarz/{ print "match" }' test.txt
match
für die folgende Datei test.txt

Code: Alles auswählen

weiss
sep
schwarz
obwohl "weiss" und "schwarz" durch den Separator "sep" getrennt sind? Ich bin etwas verwirrt.

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 20:20:23

Gute Frage. Ich habe heute Nachmittag nicht weiter getestet.

Nach meinen Tests jetzt komme ich zu der Ansicht:

- Wenn man den RS als String setzt, z.B.: RS="sep", dann verhaelt es sich so wie du erwartest.

- Wenn man den RS als RE setzt, dann scheint es voellig egal zu sein, was fuer eine RE es ist, selbst wenn sie leer ist, die ganze Datei wird dann genau ein Record.


Damit kannst du dir die Records und ihre Laengen auflisten lassen:

Code: Alles auswählen

awk 'BEGIN{RS=//} { print NR, length($0) }'

Edit: Interessanterweise wird der String (!), der RS zugewiesen wird, als RE interpretiert. Probiere also mal das aus: RS="s.p"
Use ed once in a while!

Huo
Beiträge: 412
Registriert: 26.11.2017 14:03:31

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Huo » 22.09.2022 21:21:48

Meillo hat geschrieben: ↑ zum Beitrag ↑
22.09.2022 20:20:23
Edit: Interessanterweise wird der String (!), der RS zugewiesen wird, als RE interpretiert. Probiere also mal das aus: RS="s.p"
Tatsächlich, das hätte ich nicht unbedingt erwartet. Wobei drei unterschiedliche awk-Implementierungen mit Deiner RE ".^" drei unterschiedliche Ausgaben erzeugen:

Code: Alles auswählen

$ gawk 'BEGIN{RS=".^"} /schwarz.*weiss|weiss.*schwarz/{ print "match" }' test.txt
$ mawk 'BEGIN{RS=".^"} /schwarz.*weiss|weiss.*schwarz/{ print "match" }' test.txt
match
$ nawk 'BEGIN{RS=".^"} /schwarz.*weiss|weiss.*schwarz/{ print "match" }' test.txt
nawk: syntax error in regular expression .^ at 
 source line number 1
$
Edit: Mit dem leeren String, also RS="", geben alle awk-Implementierungen das gewünschte "match" aus.

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

Re: RegEx: Zeichenfolgen an unterschiedlichen Stellen in der Datei

Beitrag von Meillo » 22.09.2022 21:50:41

Ich denke, es sollte klar sein, dass wir uns an dieser Stelle im implementierungsabhaengigen Raum abseits von POSIX befinden. Insofern sind die unterschiedlichen Verhalten nicht ueberraschend.


Generell muss man die zwei verschiedenen Arten von Quotes in awk unterscheiden. Es gibt Strings, die in Double-Quotes eingefasst sind, und es gibt REs, die in Slashes eingefasst sind. Man sieht das beispielswiese wenn man substituiert:

Code: Alles auswählen

sub(/re/, "repl")
Allerdings kann man REs auch als Strings uebergeben. Dennoch werden die unterschiedlichen Quotes unterschiedlich behandelt. Daher kommt der Unterschied zwischen RS="..." und RS=/.../.

Was von POSIX definiert ist, ist dieser Fall: RS="" -- Weist man einen leeren *String* zu, dann separiert awk an Leerzeilen (und der FS ist Newline). Das ist in der Manpage auch beschrieben.

Was passiert, wenn man RS eine RE zuweist, habe ich bisher noch nirgends beschrieben gesehen. Vielleicht muesste man dazu mal in den Code von gawk graben (oder in meinen Buechern stoebern). Was ich reverse-engineere ist, dass dann gawk ueberhaupt nicht separiert, sondern alles in einen Record packt.

Was ich oben zu der nicht matchenden RE geschrieben habe, ist irrelevant. Ich habe festgestellt, dass es (bei gawk) egal ist, was man reinschreibt. Also immer wenn man Slashes verwendet beim Zuweisen von RS, dann wird der ganze Input ein Record.


Laut POSIX kann anscheinend nur ein einziges Zeichen als RS verwendet werden. Gawk verhaelt sich hier abweichend, indem es den an RS zugewiesenen *String* als RE interpretiert.


... alles in allem ein spannendes Thema. Leider habe ich morgen wenig Zeit, aber vielleicht kann ich am Wochenende nochmal tiefer graben. :-)
Use ed once in a while!

Antworten