RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 30.05.2022 12:01:47

Kursuebersicht


Teil 07: Anker, Praezedenz, Zusammenfassung


Kommen wir nun zu den letzten zwei Metazeichen von EREs: Anker. Mit ihnen kann man einen Ausdruck am Zeilenanfang bzw. Zeilenende verankern. Anker sind reine Metainformation, sie matchen auf keine Zeichen, sondern sagen nur etwas darueber aus, wo auf der Zeile der Ausdruck gematcht werden kann.

Es gibt zwei Anker in EREs:

Der Zeilenanfangsanker wir mit dem Circumflex (^) notiert. Die RE:

Code: Alles auswählen

^foo
matcht ``foo'' nur wenn es am Zeilenanfang steht.

Der Zeilenendeanker wird mit dem Dollarzeichen ($) notiert. Die RE:

Code: Alles auswählen

bar$
matcht ``bar'' nur wenn es am Zeilenende steht.

Im Falle von EREs ist es erlaubt, diese Anker auch in die Mitte einer RE einzubauen, auch wenn sie dort niemals matchen koennen. (Das ist bei anderen RE-Varianten anders.)


Damit haben wir nun alle Funktionen von POSIX EREs kennengelernt, lediglich Collation Symbols ([. .]) und Equivalence Class Expressions ([= =]) innerhalb von Zeichenklassen haben wir ausgelassen. Diese habe ich bislang auch noch nie in der Praxis gesehen.

POSIX-Beschreibung von EREs:
https://pubs.opengroup.org/onlinepubs/9 ... #tag_09_04


Praezedenz (von hoch zu niedrig)

1. Collation-related bracket symbols innerhalb von Zeichenklassen
[==] [::] [..]

2. Escapete Metazeichen
\

3. Zeichenklassen
[]

4. Unterausdruecke
()

5. Quantoren
* + ? {m,n}

6. Verkettung
(ohne Operator)

7. Anker
^ $

8. Alternation
|

Mit Unterausdruecken (runden Klammern) kann man die Praezedenz anpassen.



Zum Schluss moechte ich nochmal Henry Spencers RE-Beschreibung aus der Manpage regex(7) hervorholen. Anmerkungen blau in eckingen Klammern.
Manpage regex(7) hat geschrieben: Regular expressions ("RE"s), as defined in POSIX.2, come
in two forms: modern REs (roughly those of egrep; POSIX.2
calls these "extended" REs) and [...BREs...].

POSIX.2 leaves
some aspects of RE syntax and semantics open; "(!)" marks
decisions on these aspects that may not be fully portable
to other POSIX.2 implementations.

A (modern) RE is one(!) or more nonempty(!) branches,
separated by '|'. It matches anything that matches one
of the branches.
[= Alternation]

A branch is one(!) or more pieces, concatenated. It
matches a match for the first, followed by a match for
the second, etc.
[= Verkettung]

A piece is an atom possibly followed by a single(!) '*',
'+', '?', or bound. An atom followed by '*' matches a
sequence of 0 or more matches of the atom. An atom fol‐
lowed by '+' matches a sequence of 1 or more matches of
the atom. An atom followed by '?' matches a sequence of
0 or 1 matches of the atom.
[= Quantoren]

A bound is '{' followed by an unsigned decimal integer,
possibly followed by ',' possibly followed by another
unsigned decimal integer, always followed by '}'. The
integers must lie between 0 and RE_DUP_MAX (255(!))
inclusive, and if there are two of them, the first may
not exceed the second. An atom followed by a bound con‐
taining one integer i and no comma matches a sequence of
exactly i matches of the atom. An atom followed by a
bound containing one integer i and a comma matches a
sequence of i or more matches of the atom. An atom fol‐
lowed by a bound containing two integers i and j matches
a sequence of i through j (inclusive) matches of the
atom.
[= Quantoren]

An atom is a regular expression enclosed in "()" (match‐
ing a match for the regular expression), an empty set of
"()" (matching the null string)(!),
[= Unterausdruck]
a bracket expression (see below),
[= Zeichenklasse]
'.' (matching any single character),
[= Punkt]
'^' (matching the null string at the beginning of a line),
'$' (matching the null string at the end of a line),
[= Anker]
a '\' followed by one of the characters "^.[$()|*+?{\"
(matching that character taken as an ordinary character),
a '\' followed by any other character(!) (matching that
character taken as an ordinary character, as if the '\'
had not been present(!)),
[= escapetes Metazeichen]
or a single character with no
other significance (matching that character).
[= literales Zeichen]
A '{' fol‐
lowed by a character other than a digit is an ordinary
character, not the beginning of a bound(!). It is ille‐
gal to end an RE with '\'.

A bracket expression is a list of characters enclosed in
"[]". It normally matches any single character from the
list (but see below). If the list begins with '^', it
matches any single character (but see below) not from the
rest of the list. If two characters in the list are sep‐
arated by '-', this is shorthand for the full range of
characters between those two (inclusive) in the collating
sequence, for example, "[0-9]" in ASCII matches any deci‐
mal digit. It is illegal(!) for two ranges to share an
endpoint, for example, "a-c-e". Ranges are very collat‐
ing-sequence-dependent, and portable programs should
avoid relying on them.
[= Zeichenklasse]

To include a literal ']' in the list, make it the first
character (following a possible '^'). To include a lit‐
eral '-', make it the first or last character, or the
second endpoint of a range. To use a literal '-' as the
first endpoint of a range, enclose it in "[." and ".]"
to make it a collating element (see below). With the
exception of these and some combinations using '[' (see
next paragraphs), all other special characters, including
'\', lose their special significance within a bracket
expression.
[= Zeichenklasse]

Within a bracket expression, a collating element (a char‐
acter, a multicharacter sequence that collates as if it
were a single character, or a collating-sequence name for
either) enclosed in "[." and ".]" stands for the sequence
of characters of that collating element. The sequence is
a single element of the bracket expression's list. A
bracket expression containing a multicharacter collating
element can thus match more than one character, for exam‐
ple, if the collating sequence includes a "ch" collating
element, then the RE "[[.ch.]]*c" matches the first five
characters of "chchcc".
[= Collating Symbol (weggelassen)]

Within a bracket expression, a collating element enclosed
in "[=" and "=]" is an equivalence class, standing for
the sequences of characters of all collating elements
equivalent to that one, including itself. (If there are
no other equivalent collating elements, the treatment is
as if the enclosing delimiters were "[." and ".]".) For
example, if o and ^ are the members of an equivalence
class, then "[[=o=]]", "[[=^=]]", and "[o^]" are all syn‐
onymous. An equivalence class may not(!) be an endpoint
of a range.
[= Equivalence Class Expression (weggelassen)]

Within a bracket expression, the name of a character
class enclosed in "[:" and ":]" stands for the list of
all characters belonging to that class. Standard charac‐
ter class names are:

alnum digit punct
alpha graph space
blank lower upper
cntrl print xdigit

These stand for the character classes defined in
wctype(3). A locale may provide others. A character
class may not be used as an endpoint of a range.
[= Zeichenklassenausdruecke]

In the event that an RE could match more than one sub‐
string of a given string, the RE matches the one starting
earliest in the string. If the RE could match more than
one substring starting at that point, it matches the
longest. Subexpressions also match the longest possible
substrings, subject to the constraint that the whole
match be as long as possible, with subexpressions start‐
ing earlier in the RE taking priority over ones starting
later. Note that higher-level subexpressions thus take
priority over their lower-level component subexpressions.
[= Gierigkeit; Der laengste der fruehesten Treffer]

Match lengths are measured in characters, not collating
elements. A null string is considered longer than no
match at all. For example, "bb*" matches the three mid‐
dle characters of "abbbc", "(wee|week)(knights|nights)"
matches all ten characters of "weeknights", when "(.*).*"
is matched against "abc" the parenthesized subexpression
matches all three characters, and when "(a*)*" is matched
against "bc" both the whole RE and the parenthesized sub‐
expression match the null string.
[= Gierigkeit; Der laengste der fruehesten Treffer]


Moeglicherweise weitere erwartete Features (wie beispielsweise die Wortgrenzenanker \< \>) sind nicht Teil von EREs (was schon daran ersichtlich ist, dass bei EREs alle Metazeichen unescapet sind ;-) ). Dass sie dennoch bei egrep funktionieren, liegt daran, dass egrep nicht strikt POSIX entspricht, sondern verschiedene Erweiterungen und Abweichungen hat. Auf solche und weitere Features werden wir in spaeteren Kapiteln dieses Kurses noch eingehen. Das Kapitel EREs wird hiermit erstmal geschlossen.



Aufgaben:

1) Schreibe eine RE die immer matcht.

2) Schreibe eine RE die niemals matcht.

3) Schreibe eine RE die leere Zeilen matcht.

4) Filtere alle leere Zeilen weg.

5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?
Use ed once in a while!

Benutzeravatar
tegula
Beiträge: 439
Registriert: 04.06.2004 13:51:04
Lizenz eigener Beiträge: MIT Lizenz

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von tegula » 01.06.2022 12:50:08

Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
1) Schreibe eine RE die immer matcht.

Code: Alles auswählen

$ egrep -o --line-number "(.)*" schwaebische-kunde.txt
Ausgabe: NoPaste-Eintrag41694, Zeile 17 - 76.
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
2) Schreibe eine RE die niemals matcht.

Code: Alles auswählen

$ egrep -o --line-number "()" schwaebische-kunde.txt
$ 
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
3) Schreibe eine RE die leere Zeilen matcht.

Code: Alles auswählen

$ # Aufgabe 3: "Schreibe eine RE die leere Zeilen matcht."
$
$ ## Eingabe anzeigen
$ cat -n a3.txt
     1  Das
     2
     3
     4  ist
     5
     6
     7  ein Text
     8
     9
    10  mit vielen
    11
    12  zeilen.
    13
    14
    15
    16  Wirklich!
$
$ ## Leere Zeilen verstanden als: Zeilen, die gar keine Zeichen enthalten
$ egrep --line-number "^(()*)$" a3.txt
2:
3:
5:
6:
8:
9:
11:
13:
14:
15:
$
$ ## Leere Zeilen verstanden als: Zeilen, die entweder gar keine Zeichen enthalten oder ausschließlich nicht-druckbare Zeichen halten
$ egrep --line-number "^([^[:print:]]*)$" a3.txt
2:
3:
5:
6:
8:
9:
11:
13:
14:
15:
$
$ ## Leere Zeilen verstanden als: Zeilen, die entweder gar keine Zeichen enthalten oder ausschließlich Leerzeichen bzw. Tabulator-Zeichen enthalten
$ egrep --line-number "^([[:blank:]]*)$" a3.txt
2:
3:
5:
6:
8:
9:
11:
13:
14:
15:
$ 
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
4) Filtere alle leere Zeilen weg.

Code: Alles auswählen

$ # Aufgabe 4: "Filtere alle leere Zeilen weg".
$ ## Eingabe anzeigen
$ cat -n a3.txt
     1  Das
     2
     3
     4  ist
     5
     6
     7  ein Text
     8
     9
    10  mit vielen
    11
    12  zeilen.
    13
    14
    15
    16  Wirklich!
$
$ ## egrep ausführen bzw. RE anwenden
$ egrep -o --line-number "^[[:print:]]+$" a3.txt
1:Das
4:ist
7:ein Text
10:mit vielen
12:zeilen.
16:Wirklich!
$ 
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?
Der vor dem Circumflex befindliche Teilausdruck muss so beschaffen sein, dass er (auch) auf 'kein Zeichen' () matched (siehe folgende Beispiele)

Code: Alles auswählen

$ # Eingabe
$ cat -n a5.txt
     1  groß ist das haus.
     2  das haus ist groß.
     3  dieses haus ist das größte.
$
$ # Beispiele
$ egrep -o --line-number "(.)*^das haus" a5.txt
2:das haus
$ egrep -o --line-number "(blablabla)*^das haus" a5.txt
2:das haus
$ egrep -o --line-number "(hallo){0,17}^das haus" a5.txt
2:das haus
$ 

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 01.06.2022 13:55:31

Ich habe meine Lösungen mit dem aus früheren Übungen bekannten Text schwaebische-kunde.txt getestet, der praktischerweise nach der Überschrift eine Leerzeile enthält.
1) Schreibe eine RE, die immer matcht.

Code: Alles auswählen

$ egrep '.*' schwaebische-kunde.txt
Schwaebische Kunde.

Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
Erläuterung: Jede Zeile enthält beliebig viele oder – falls Leerzeile – keine Zeichen (siehe auch Aufgabe 1 der 6. Lektion).

Zweite Lösung:

Code: Alles auswählen

$ egrep '^' schwaebische-kunde.txt 
Schwaebische Kunde.

Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
Erläuterung: Jede Zeile, auch die Leerzeile, hat einen Zeilenanfang. Unterschied zur ersten Lösung: egrep -o matcht nichts.

Dritte Lösung:

Code: Alles auswählen

$ egrep '' schwaebische-kunde.txt
Schwaebische Kunde.

Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
Erläuterung: Der leere String matcht alle Zeilen, inklusive der Leerzeile. Die leere Menge ist die einzige Teilmenge, die allen Mengen gemeinsam ist. Ins Mystische gewendet: Das Nichts ist nirgends und überall :wink: . Auch hier matcht egrep -o nichts.
2) Schreibe eine RE die niemals matcht.

Code: Alles auswählen

$ egrep '.^' schwaebische-kunde.txt
$
Erläuterung: Einem Zeilenanfang kann (in der gleichen Zeile) kein Zeichen vorangehen. Analog funktioniert '$.'. Man könnte statt des Punktes auch irgendein Zeichen nehmen: 'a^' oder '$a'.
3) Schreibe eine RE die leere Zeilen matcht.

Code: Alles auswählen

$ egrep '^$' schwaebische-kunde.txt

$
Erläuterung: Leere Zeilen sind genau die Zeilen, in denen zwischen Zeilenanfang und -ende kein Zeichen steht.
4) Filtere alle leere Zeilen weg.

Code: Alles auswählen

$ egrep '.+' schwaebische-kunde.txt
Schwaebische Kunde.
Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
Erläuterung: Alle Zeilen, die mindestens ein Zeichen enthalten, werden gematcht, also genau die Leerzeilen weggefiltert.
5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?
Zuerst fiel mir eine Lösung ein, die zwar unsinnig ist, aber funktioniert :

Code: Alles auswählen

$ egrep '.?^Daselbst' schwaebische-kunde.txt
Daselbst erhub sich grosse Noth,
Erläuterung: Da ".?" auch für kein Zeichen stehen kann, darf es überflüssigerweise vor dem Circumflex stehen.

Eine sinnvollere Lösung mit Alternation:

Code: Alles auswählen

$ egrep 'nach[[:punct:]]?$|^Nach' schwaebische-kunde.txt
Er zog es nur am Zaume nach,
Nach ihm zu werfen mit den Spiessen.
Erläuterung: Es werden alle Zeilen gematcht, die mit "nach" (evtl. gefolgt von einem Satzzeichen) enden oder mit "Nach" beginnen.

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 01.06.2022 15:32:56

tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08

Code: Alles auswählen

$ ## Leere Zeilen verstanden als: Zeilen, die entweder gar keine Zeichen enthalten oder ausschließlich Leerzeichen bzw. Tabulator-Zeichen enthalten
$ egrep --line-number "^([[:blank:]]*)$" a3.txt
Gute Idee, Leerzeilen alternativ auch in einem weiter verstandenen Sinn zu matchen. Kann mir vorstellen, dass das auch in der täglichen Praxis relevant sein kann.

Du bei Aufgabe 2 ("RE, die niemals matcht"):
tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08

Code: Alles auswählen

$ egrep -o --line-number "()" schwaebische-kunde.txt
Ich bei Aufgabe 1 ("RE, die immer matcht"):
Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 13:55:31

Code: Alles auswählen

$ egrep '' schwaebische-kunde.txt
Witzig, dass wir die gleiche RE (nur in unterschiedlicher Schreibweise) für geradezu gegensätzliche Zwecke benutzen möchten. Du verwendest den leeren String, um niemals zu matchen, ich, um immer zu matchen 8O . Der Unterschied ist natürlich, dass Du egrep mit der Option -o benutzt.

EDIT: Nach einigem Nachdenken neige ich der Meinung zu, dass nur tegulas Lösung der Aufgabenstellung (in dem Fall zu Aufgabe 2) gerecht wird. Meine Lösung zu Aufgabe 1 gibt zwar den gesamten Text aus, matcht aber nichts. Es gibt bei grep einen Unterschied zwischen Output (ganze Zeilen oder eben nichts) und Match (die gefundenen Teile), und letzteres war in den Aufgaben 1 und 2 gefragt.

EDIT 2: Wenn allerdings egrep (ohne -o) mit der RE für den leeren String alle Zeilen ausgibt, muss die RE logischerweise etwas matchen und das ist der leere String. Also matcht tegulas Lösung zu Aufgabe 2 nicht nichts, sondern den leeren String, und ist deshalb strenggenommen auch keine korrekte Lösung. :P

EDIT 3 (jetzt hoffentlich der letzte :mrgreen: ): Und da Aufgabe 1 ja nach einer RE fragt, die "immer matcht", aber nicht nach einer, die alles matcht, müsste meine Lösung mit dem leeren String eigentlich doch passen, auch wenn sie nichts Sichtbares matcht. 8)

Benutzeravatar
tegula
Beiträge: 439
Registriert: 04.06.2004 13:51:04
Lizenz eigener Beiträge: MIT Lizenz

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von tegula » 02.06.2022 18:22:09

Danke für deine tollen Erläuterungen und Überlegungen! :THX:
Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 15:32:56
Du bei Aufgabe 2 ("RE, die niemals matcht"):
tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08

Code: Alles auswählen

$ egrep -o --line-number "()" schwaebische-kunde.txt
Ich bei Aufgabe 1 ("RE, die immer matcht"):
Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 13:55:31

Code: Alles auswählen

$ egrep '' schwaebische-kunde.txt
Witzig, dass wir die gleiche RE (nur in unterschiedlicher Schreibweise) für geradezu gegensätzliche Zwecke benutzen möchten. Du verwendest den leeren String, um niemals zu matchen, ich, um immer zu matchen 8O . Der Unterschied ist natürlich, dass Du egrep mit der Option -o benutzt.

EDIT: Nach einigem Nachdenken neige ich der Meinung zu, dass nur tegulas Lösung der Aufgabenstellung (in dem Fall zu Aufgabe 2) gerecht wird. Meine Lösung zu Aufgabe 1 gibt zwar den gesamten Text aus, matcht aber nichts. Es gibt bei grep einen Unterschied zwischen Output (ganze Zeilen oder eben nichts) und Match (die gefundenen Teile), und letzteres war in den Aufgaben 1 und 2 gefragt.

EDIT 2: Wenn allerdings egrep (ohne -o) mit der RE für den leeren String alle Zeilen ausgibt, muss die RE logischerweise etwas matchen und das ist der leere String. Also matcht tegulas Lösung zu Aufgabe 2 nicht nichts, sondern den leeren String, und ist deshalb strenggenommen auch keine korrekte Lösung. :P
Scheinbar ist meine Lösung nicht nur "streng genommen" falsch, sondern funktioniert so tatsächlich nur mit egrep. Mit Debianr-cran-stringr matched sie hingegen alle Zeichen -- das heißt: Sie funktioniert nicht :(.
--> Veranschaulichung als Tabelle: NoPaste-Eintrag41698.

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 02.06.2022 20:12:01

tegula hat geschrieben: ↑ zum Beitrag ↑
02.06.2022 18:22:09
Scheinbar ist meine Lösung nicht nur "streng genommen" falsch, sondern funktioniert so tatsächlich nur mit egrep.
In Deinem Sinne funktioniert sie mit egrep -o. Wobei ich inzwischen soweit bin, einzusehen, dass egrep mit Option "-o" und egrep ohne Option "-o" grundsätzlich immer das gleiche matchen und sich nur in der Ausgabe unterscheiden.
tegula hat geschrieben: ↑ zum Beitrag ↑
02.06.2022 18:22:09
Mit Debianr-cran-stringr matched sie hingegen alle Zeichen -- das heißt: Sie funktioniert nicht :(.
--> Veranschaulichung als Tabelle: NoPaste-Eintrag41698.
Hm, meine Vermutung: Auch r-cran-stringr matcht für unsere REs nicht die Zeichen, sondern eben den leeren String. Deine Tabelle hat mich jetzt allerdings um die Erkenntnis bereichert, dass manche Programmiersprachen die leeren Substrings in einem String offenbar "zählen" können: Anzahl der Zeichen plus 2 (bzw. plus 1 für meine Schreibweise der RE?). Demnach versteckt sich wohl zwischen jeweils benachbarten Zeichen sowie am Anfang und/oder Ende einer Zeile jeweils ein leerer String. Kann ich auch mit Python reproduzieren:

Code: Alles auswählen

>>> Z = "Schwaebische Kunde."
>>> Z.count('')
20
Aber ich glaube, mit einer solchen Zählerei verstrickt man sich unweigerlich in Paradoxien. Man könnte nämlich mit Recht auch behaupten: Zwischen benachbarten Zeichen stehen beliebig viele leere Strings, z.B. fünf:

Code: Alles auswählen

$ egrep -o 'N(){5}ach' schwaebische-kunde.txt
Nach
Meillo wird sich sicher gut unterhalten – oder enttäuscht sein, dass wir nicht über Anker diskutieren. :wink:

TuxPeter
Beiträge: 1961
Registriert: 19.11.2008 20:39:02
Lizenz eigener Beiträge: MIT Lizenz

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von TuxPeter » 03.06.2022 10:20:53

Hi,
habe Eure Lösungen überflogen, etwas pausiert + sich setzen gelassen, danach meine Versuche gemacht:

1) Schreibe eine RE die immer matcht.
egrep -n ".*" txtfile
(Irgendwas keinmal oder vielmals)

2) Schreibe eine RE die niemals matcht.
egrep -n "$." txtfile
(Nach dem Ende kann nix mehr kommen)

3) Schreibe eine RE die leere Zeilen matcht.
egrep -n "^$" txtfile
(Steht nichts zwischen Anfang und Ende)

4) Filtere alle leere Zeilen weg.
egrep -n "^.+" txtfile
(= zeige nur die mit Inhalt)

Anmerkungen:
Die RE-Eingaben funktionieren teilweise auch ohne Quotes.
Zu 5) habe ich keine Lösung gefunden.

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

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 03.06.2022 11:41:31

Ich will mal ein paar Dinge kommentieren ...

tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
1) Schreibe eine RE die immer matcht.

Code: Alles auswählen

$ egrep -o --line-number "(.)*" schwaebische-kunde.txt
Mir ist auch schon frueher aufgefallen, dass du recht oft unnoetige Klammern verwendest. Vermutlich ist es fuer dich dann einfacher zu erkennen auf was der Quantor wirkt. Im aktuellen Teil siehst du anhand der Praezendenztabelle auf was sich ein Quantor bezieht: Alles was ueber den Quantoren versteht er als Einheit.

Also ein Einzelzeichen, ein escapetes Zeichen, eine Zeichenklasse und ein Unterausdruck. Anker, Verkettungen und Alternationen aber nicht. D.h. in ``abc*'' (= Verkettung) bezieht sich der Stern nur auf das `c'. In ``\.*'' bezieht er sich dagegen auf den escapeten und damit literal zu matchenden Punkt (also die zwei Zeichen vor dem Stern, weil das escapete Zeichen vom Stern als eine Einheit angesehen wird).

In deinem konkreten Fall kannst du statt ``(.)*'' also ``.*'' schreiben. Das solltest du dir auch angewoehnen, weil das erstens alle so machen und du zweitens dir das Leben leichter machst wenn du Klammern nur an noetigen Stellen verwendest wenn wir spaeter einfangende Klammern und Rueckbezuege in BREs und PCREs kennenlernen.

tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08
Meillo hat geschrieben: ↑ zum Beitrag ↑
30.05.2022 12:01:47
5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?
Der vor dem Circumflex befindliche Teilausdruck muss so beschaffen sein, dass er (auch) auf 'kein Zeichen' () matched (siehe folgende Beispiele)

Code: Alles auswählen

$ # Eingabe
$ cat -n a5.txt
     1  groß ist das haus.
     2  das haus ist groß.
     3  dieses haus ist das größte.
$
$ # Beispiele
$ egrep -o --line-number "(.)*^das haus" a5.txt
2:das haus
$ egrep -o --line-number "(blablabla)*^das haus" a5.txt
2:das haus
$ egrep -o --line-number "(hallo){0,17}^das haus" a5.txt
2:das haus
$ 
Das ist eine gute Idee: Wir setzen einen optionalen Ausdruck von den Zeilenanfangsanker. Falls der optionale Ausdruck (bei dem voellig egal ist wie er lautet) matcht, dann kann die Gesamt-RE nicht matchen, also kann die Gesamt-RE nur matchen wenn der optionale Ausdruck den leeren String matcht also nullmal angewendet wird.

Es gibt aber auch eine weniger irritierende Variante als diese. (Huo denkt da auch noch zu kompliziert.)


Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 15:32:56
Du bei Aufgabe 2 ("RE, die niemals matcht"):
tegula hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 12:50:08

Code: Alles auswählen

$ egrep -o --line-number "()" schwaebische-kunde.txt
Ich bei Aufgabe 1 ("RE, die immer matcht"):
Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 13:55:31

Code: Alles auswählen

$ egrep '' schwaebische-kunde.txt
Witzig, dass wir die gleiche RE (nur in unterschiedlicher Schreibweise) für geradezu gegensätzliche Zwecke benutzen möchten. Du verwendest den leeren String, um niemals zu matchen, ich, um immer zu matchen 8O . Der Unterschied ist natürlich, dass Du egrep mit der Option -o benutzt.
Ihr solltet sowohl mit einer komplett leeren RE als auch mit einem leeren Unterausdruck etwas aufpassen. Ihr verwendet die recht oft. In der Praxis sind diese beiden Faelle normalerweise implementierungsabhaengig also undefiniert. Was mit GNU egrep in Version x funktioniert kann mit anderen Versionen, anderen Implementierungen von egrep oder anderen Programmen/RE-Libs anders funktionieren. Ich wuerde versuchen leere REs und leere Unterausdruecke nur in dem einen Fall einsetzen wenn ich es nicht anders loesen kann. (Beispielsweise FS in awk bei dem FS="" nach jedem Zeichen splittet.)
Huo hat geschrieben: ↑ zum Beitrag ↑
01.06.2022 15:32:56
EDIT 3 (jetzt hoffentlich der letzte :mrgreen: ): Und da Aufgabe 1 ja nach einer RE fragt, die "immer matcht", aber nicht nach einer, die alles matcht, [...]
Da ist dir ein relevanter Unterschied aufgefallen, der auch fuer manche der anderen Aufgaben hier wertvoll ist.


Huo hat geschrieben: ↑ zum Beitrag ↑
02.06.2022 20:12:01
tegula hat geschrieben: ↑ zum Beitrag ↑
02.06.2022 18:22:09
Scheinbar ist meine Lösung nicht nur "streng genommen" falsch, sondern funktioniert so tatsächlich nur mit egrep.
In Deinem Sinne funktioniert sie mit egrep -o. Wobei ich inzwischen soweit bin, einzusehen, dass egrep mit Option "-o" und egrep ohne Option "-o" grundsätzlich immer das gleiche matchen und sich nur in der Ausgabe unterscheiden.
Das stimmt fast ganz. Die einfache Antwort ist: Ja, `-o' veraendert nur die Ausgabe.

Die etwas genauere Antwort ist: Ohne `-o' interessiert nur, *ob* eine Zeile matcht, aber es ist egal ob sie oefters matcht. Bei `-o' ist das aber relevant. Fuer jeden Match wird eine Ausgabe geschrieben. Mit `-o' koennen dadurch auch mehrere Ausgaben fuer eine Zeile raus kommen, siehe:

Code: Alles auswählen

:-Q echo aaa | egrep .  
aaa

:-Q echo aaa | egrep -o .
a
a
a
Da egrep aber eigentlich nur zeilenbasiert arbeitet ist `-o' strukturell gesehen ein Sonderfall. Das zeigt sich wenn man `-c' (= count matching lines) anschaut -- das kann nur Zeilen zaehlen, selbst wenn man `-o' verwendet (welches dann einfach ignoriert wird).


TuxPeter hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 10:20:53
4) Filtere alle leere Zeilen weg.
egrep -n "^.+" txtfile
(= zeige nur die mit Inhalt)
Wenngleich diese Loesung korrekt ist, wie auch die anderen Loesungen zu der Ausgabe, so sind sie alle unnoetig kompliziert. Hier koennt ihr nun euer Denken etwas ueben. Beginnt damit, euch von der konkreten Aufgabenformulierung zu loesen und nachzudenken was sie logisch bedeutet. Dann denkt an Huos Erkenntnis, dass man unterscheiden sollte *ob* eine Zeile matcht und *was* sie matcht. Manchmal ist das eine, manchmal das andere relevant. Vielleicht kommt ihr noch auf eine viel einfachere Loesung. ;-)


Auch bei Aufgabe 5 koenntet ihr noch versuchen, eine klarere und simplere Loesung zu finden.
Use ed once in a while!

Benutzeravatar
tegula
Beiträge: 439
Registriert: 04.06.2004 13:51:04
Lizenz eigener Beiträge: MIT Lizenz

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von tegula » 03.06.2022 13:52:34

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Mir ist auch schon frueher aufgefallen, dass du recht oft unnoetige Klammern verwendest. Vermutlich ist es fuer dich dann einfacher zu erkennen auf was der Quantor wirkt. Im aktuellen Teil siehst du anhand der Praezendenztabelle auf was sich ein Quantor bezieht: Alles was ueber den Quantoren versteht er als Einheit.
[...]
In deinem konkreten Fall kannst du statt ``(.)*'' also ``.*'' schreiben. Das solltest du dir auch angewoehnen, weil das erstens alle so machen und du zweitens dir das Leben leichter machst wenn du Klammern nur an noetigen Stellen verwendest wenn wir spaeter einfangende Klammern und Rueckbezuege in BREs und PCREs kennenlernen.
Danke für deinen Hinweis und deine Erklärung, weshalb mein bisheriges Vorgehen ("lieber zu viel als zu wenig Klammern") problematisch ist. Da ich vor diesem Kurs keinen Kontakt mit regulären Ausdrücken hatte, bin fälschlicherweise (bis eben) davon ausgegangen, dass redunante Klammern nicht schaden. Ich habe bisher mit zusätzlichen Klammern folgende Ziele verfolgt: 1.) Übersichtlichere (Gesamt-)Ausdrücke. 2.) Fehler bei der Anwendung von Vorrangregeln vermeiden.
Gibt es Fälle, in denen zusätzliche Klammern unproblematisch sind? Oder sind sie immer problematisch? Gibt es Tricks um die beiden o.g. Ziele ohne Klammern zu erreichen?
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Also ein Einzelzeichen, ein escapetes Zeichen, eine Zeichenklasse und ein Unterausdruck. Anker, Verkettungen und Alternationen aber nicht. D.h. in ``abc*'' (= Verkettung) bezieht sich der Stern nur auf das `c'. In ``\.*'' bezieht er sich dagegen auf den escapeten und damit literal zu matchenden Punkt (also die zwei Zeichen vor dem Stern, weil das escapete Zeichen vom Stern als eine Einheit angesehen wird).
Danke! Ich es mir jetzt folgendermaßen gemerkt, bin mir aber sehr unsicher, ob ich deine Erklärung richtig verstanden habe :oops: :
Nur ein Zeichen (z. B. "c" oder "\.") oder eine Zeichenklassen (z. B. [abc]) als "Operand" des Quantors --> Keine Klammern setzen.

Code: Alles auswählen

"c+"
"[abc]?"
"\.{2,3}"
Mehre Zeichen (z.B. "abc") oder eine Alternation (z. B. "a|b|c") oder ein Teilausdruck mit Ankern (z. B. "ab.\<heute") als "Operand" des Quantors --> Klammer setzen.

Code: Alles auswählen

"(abc)?"
"(a|b|c){1,5}"
"(ab.\<heute)+"
Stimmt das so?

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

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 03.06.2022 14:11:01

tegula hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 13:52:34
Ich habe bisher mit zusätzlichen Klammern folgende Ziele verfolgt: 1.) Übersichtlichere (Gesamt-)Ausdrücke. 2.) Fehler bei der Anwendung von Vorrangregeln vermeiden.
Gibt es Fälle, in denen zusätzliche Klammern unproblematisch sind? Oder sind sie immer problematisch? Gibt es Tricks um die beiden o.g. Ziele ohne Klammern zu erreichen?
Deine Motivation kann ich verstehen, das geht vielen Anfaengern so. Das gibt sich mit der Zeit wenn du lernst, REs schneller zu erfassen. In einer Matheformel klammerst du ja auch nicht alles was sich klammern laesst, sondern nur dort wo es noetig ist ... und in sehr unuebersichtlichen Situationen vielleicht zusaetzlich. Wenn du zukuenftig oefters mal REs von Profis liest, dann merkst du schnell was ueblich ist und was nicht.

Wenn du BREs (z.B. in `grep' und `sed') kennenlernst, dann werden dir Klammern nicht mehr so uebersichtlich vorkommen, da man sie dort escapen muss, damit sie zu Metazeichen werden. Dort waere es dann:

Code: Alles auswählen

grep '\(.\)*'
was doch deutlich weniger lesbar ist als:

Code: Alles auswählen

grep '.*'
Hier sieht man nun auch, dass man bei Klammern aufpassen muss in welcher RE-Art man sich befindet, waehrend der Ausdruck ohne Klammern bei EREs und BREs gleich ist (nicht jeder ist es aber Zeichenklassen, Punkt, Stern und Anker schon).

Problematisch sind Klammern nicht. Man kann sie zusaetzlich setzen. Nur in einem Fall sind sie nachteilig: Wenn man Rueckverweise hat, dann koennen einem die Zahlen ausgehen, falls man nur auf 9 Stueck zugreifen kann, bzw. hat man dann auch unerwuenscht einfangende Klammern und es ist schwieriger die relevanten rauszuzaehlen (bei PCREs kann man sowohl einfangende als auch nur gruppierende Klammern haben).

Deine Regel hier ist gut:
tegula hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 13:52:34
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Also ein Einzelzeichen, ein escapetes Zeichen, eine Zeichenklasse und ein Unterausdruck. Anker, Verkettungen und Alternationen aber nicht. D.h. in ``abc*'' (= Verkettung) bezieht sich der Stern nur auf das `c'. In ``\.*'' bezieht er sich dagegen auf den escapeten und damit literal zu matchenden Punkt (also die zwei Zeichen vor dem Stern, weil das escapete Zeichen vom Stern als eine Einheit angesehen wird).
Danke! Ich es mir jetzt folgendermaßen gemerkt, bin mir aber sehr unsicher, ob ich deine Erklärung richtig verstanden habe :oops: :
Nur ein Zeichen (z. B. "c" oder "\.") oder eine Zeichenklassen (z. B. [abc]) als "Operand" des Quantors --> Keine Klammern setzen.

Code: Alles auswählen

"c"+
"[abc]*"
"\.{2,3}"
Mehre Zeichen (z.B. "abc") oder eine Alternation (z. B. "a|b|c") oder ein Teilausdruck mit Ankern (z. B. "ab.\<heute") als "Operand" des Quantors --> Klammer setzen.

Code: Alles auswählen

"(abc)?"
"(a|b|c){1,5}"
"(ab.\<heute)+"
Stimmt das so?
Ja.

Kurzum kann man auch sagen: Ein Quantor bezieht sich auf das Konstrukt unmittelbar davor das fuer ein einzelnes Zeichen steht (d.h. literales Zeichen, escaptes Zeichen, Zeichenklasse). Nur im Falle eines Unterausdrucks bezieht er sich auf mehrere Zeichen.

(Diese Merkregel sollte ich noch in die Quantoreneinheit aufnehmen ...)
Use ed once in a while!

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 03.06.2022 16:04:04

@Meillo: Danke für Deine ausführlichen und hilfreichen Kommentare! :THX:

Spoilerwarnung: Wer selbst noch versuchen will, die von Meillo angesprochenen simpleren Lösungen zu den Aufgaben 4 und 5 zu finden, sollte vielleicht erst einmal nicht weiterlesen. :wink:
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
TuxPeter hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 10:20:53
4) Filtere alle leere Zeilen weg.
egrep -n "^.+" txtfile
(= zeige nur die mit Inhalt)
Wenngleich diese Loesung korrekt ist, wie auch die anderen Loesungen zu der Ausgabe, so sind sie alle unnoetig kompliziert. Hier koennt ihr nun euer Denken etwas ueben. Beginnt damit, euch von der konkreten Aufgabenformulierung zu loesen und nachzudenken was sie logisch bedeutet. Dann denkt an Huos Erkenntnis, dass man unterscheiden sollte *ob* eine Zeile matcht und *was* sie matcht. Manchmal ist das eine, manchmal das andere relevant. Vielleicht kommt ihr noch auf eine viel einfachere Loesung. ;-)
Nachdem Du uns hier einen kleinen Stupps in die richtige Richtung gegeben hast, ist mir zu Aufgabe 4 tatsächlich eine ultimativ minimalistische Lösung eingefallen:

Code: Alles auswählen

$ egrep '.' schwaebische-kunde.txt 
Schwaebische Kunde.
Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
Um die Leerzeilen wegzufiltern, reicht ein einsamer Punkt völlig aus, da er ein beliebiges Zeichen matcht, also genau die Zeilen ausgibt, die keine Leerzeilen sind. Edit: Mit egrep -o kann man sich davon überzeugen, dass die RE jedes Zeichen einzeln matcht; für die Aufgabe ist es unnötig, ganze Zeilen zu matchen.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Auch bei Aufgabe 5 koenntet ihr noch versuchen, eine klarere und simplere Loesung zu finden.
Hm, zu Aufgabe 5 habe ich jetzt auch eine einfachere Lösung gefunden, bin aber nicht ganz sicher, ob die gemeint ist bzw. ob ich sie selbst richtig verstanden habe.
5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?

Code: Alles auswählen

$ egrep '?^Daselbst' schwaebische-kunde.txt
Daselbst erhub sich grosse Noth,
Hier konnte ich aus meiner ursprünglichen Lösung den Punkt vorm Fragezeichen weglassen, das so nun wohl auf einen leeren String angewendet wird. Aber würde das in jeder Implementierung funktionieren? Du selbst warnst ja:
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Ihr solltet sowohl mit einer komplett leeren RE als auch mit einem leeren Unterausdruck etwas aufpassen.

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

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 04.06.2022 08:19:58

Huo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 16:04:04
Nachdem Du uns hier einen kleinen Stupps in die richtige Richtung gegeben hast, ist mir zu Aufgabe 4 tatsächlich eine ultimativ minimalistische Lösung eingefallen:

Code: Alles auswählen

$ egrep '.' schwaebische-kunde.txt 
Schwaebische Kunde.
Als Kaiser Rothbart lobesam
Zum heil'gen Land gezogen kam,
[...]
:THX:

Huo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 16:04:04
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Auch bei Aufgabe 5 koenntet ihr noch versuchen, eine klarere und simplere Loesung zu finden.
Hm, zu Aufgabe 5 habe ich jetzt auch eine einfachere Lösung gefunden, bin aber nicht ganz sicher, ob die gemeint ist bzw. ob ich sie selbst richtig verstanden habe.
5) Wie kann der Circumflex den Zeilenanfang matchen, wenn er nicht als erstes Zeichen in der RE steht?

Code: Alles auswählen

$ egrep '?^Daselbst' schwaebische-kunde.txt
Daselbst erhub sich grosse Noth,
Hier konnte ich aus meiner ursprünglichen Lösung den Punkt vorm Fragezeichen weglassen, das so nun wohl auf einen leeren String angewendet wird. Aber würde das in jeder Implementierung funktionieren? Du selbst warnst ja:
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.06.2022 11:41:31
Ihr solltet sowohl mit einer komplett leeren RE als auch mit einem leeren Unterausdruck etwas aufpassen.
Lesbarer ist es dadurch nicht geworden und die Portabilitaet ist schlechter geworden.

Ihr befindet euch in einer Denkrinne. Alle Loesungsvorschlaege sind derzeit der Art, dass ein optionaler Ausdruck mit einem geankterten Ausdruck verkettet wird. Tretet mal einen Schritt zurueck, brecht aus der Denkrinne aus und geht die Sache nochmal struktureller an: Was fuer Moeglichkeiten haben wir denn ueberhaupt?

1. Moeglichkeit: Einen beliebigen optionalen Ausdruck vor den Anker stellen (Verkettung), der dadurch nie matchen kann und folglich effektiv ignoriert wird, z.B.:

Code: Alles auswählen

.?^Zeilenanfang
(Nur diesen Pfad habt ihr bislang verfolgt. Das ist eure Denkrinne.)

2. Moeglichkeit: Eine Alternation deren erste Alternative ein Ausdruck ist, der nie matchen kann und deshalb effektiv ignoriert wird, z.B.:

Code: Alles auswählen

matcht$nie|^Zeilenanfang
3. Moeglichkeit: ... ?

(tegula wird diese dritte Moeglichkeit wohl am schnellsten sehen. ;-) )

Es gibt auch noch eine 4. Moeglichkeit, die aber etwas ueber die bisherigen Kursinhalte hinausgeht. Ihr koenntet aber trotzdem drauf kommen ... zumindest vom strukturellen Ansatz.
Use ed once in a while!

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 04.06.2022 14:45:26

Meillo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 08:19:58
3. Moeglichkeit: ... ?

(tegula wird diese dritte Moeglichkeit wohl am schnellsten sehen. ;-) )
Ja, jetzt sehe ich auch eine ganz banale Lösung – die mir ohne Nennung von tegula nicht so schnell eingefallen wäre, weshalb ich ihm gerne den Vortritt lasse. :wink: Man könnte aber auch ohne allzu langes Nachdenken auf die Lösung kommen, wenn man sich einen Überblick über alle in REs verwendbare Sonderzeichen und deren Funktion vor Augen führt. Oder man schaue sich die in dieser Kurseinheit von Meillo gegebene Übersicht der Präzedenz-Reihenfolge nochmal an.
Meillo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 08:19:58

Es gibt auch noch eine 4. Moeglichkeit, die aber etwas ueber die bisherigen Kursinhalte hinausgeht. Ihr koenntet aber trotzdem drauf kommen ... zumindest vom strukturellen Ansatz.
Ganz vorsichtig kombiniert: Es könnte eine alternative, im Kurs noch nicht behandelte Möglichkeit geben, den Zeilenanfang zu matchen? Die stellt man dann redundant dem Circumflex voran?

Benutzeravatar
tegula
Beiträge: 439
Registriert: 04.06.2004 13:51:04
Lizenz eigener Beiträge: MIT Lizenz

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von tegula » 04.06.2022 15:18:21

Huo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 14:45:26
Meillo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 08:19:58
3. Moeglichkeit: ... ?

(tegula wird diese dritte Moeglichkeit wohl am schnellsten sehen. ;-) )
Ja, jetzt sehe ich auch eine ganz banale Lösung – die mir ohne Nennung von tegula nicht so schnell eingefallen wäre, weshalb ich ihm gerne den Vortritt lasse. :wink: Man könnte aber auch ohne allzu langes Nachdenken auf die Lösung kommen, wenn man sich einen Überblick über alle in REs verwendbare Sonderzeichen und deren Funktion vor Augen führt. Oder man schaue sich die in dieser Kurseinheit von Meillo gegebene Übersicht der Präzedenz-Reihenfolge nochmal an.
Ich passe. Dass Meillo mich explizit erwähnt, lässt mich vermuten, dass die Lösung irgendwas mit runden Klammern bzw. Unterausdrücken/Gruppierung zu tun hat. Leider bin ich trotzdem zu keiner Idee (geschweige denn Lösung) gelangt :oops: . Freue mich aber auf die Lösung von Huo :THX:.

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 04.06.2022 16:09:41

tegula hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 15:18:21
Dass Meillo mich explizit erwähnt, lässt mich vermuten, dass die Lösung irgendwas mit runden Klammern bzw. Unterausdrücken/Gruppierung zu tun hat.
Genau, die Klammern :wink: ... Man mache einfach einen unnötigen Unterausdruck auf und setze den Circumflex in Klammern:

Code: Alles auswählen

$ egrep '(^)Durch' schwaebische-kunde.txt 
Durch ein Gebirge, wuest und leer.
Durch Kopf und Leib hindurchgeschnitten.
Ich musste allerdings kurz überlegen, ob der Reguläre Ausdruck durch eine veränderte Präzedenz, die der Circumflex so evtl. erhält, anders matchen könnte. Aber da der Circumflex alleine ohne weitere Zeichen im Unterausdruck steht, ändert sich trotz Klammerung nichts wirklich an der Gruppierung. Klammert man auf den Circumflex folgende Zeichen mit ein, kann sich das Matching natürlich sehr wohl ändern:

RE matcht:

Code: Alles auswählen

$ echo "ffmpeg ist ein CLI-Programm" | egrep '^f{2}'
ffmpeg ist ein CLI-Programm
RE matcht nicht:

Code: Alles auswählen

$echo "ffmpeg ist ein CLI-Programm" | egrep '(^f){2}'
$

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

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 04.06.2022 17:07:49

Die Klammern sind was ich gemeint habe, aber es geht sogar noch einfacher: Man macht einfach (unnoetige) Klammern ganz aussen um die ganze RE rum. Da muss man dann gar nichts denken. ;-)

Code: Alles auswählen

(^irgendeine RE)
Das kann man sogar mehrfach machen:

Code: Alles auswählen

(((^irgendeine RE)))
... wahrscheinlich war der Fall zu simpel als dass ihr ihn gesehen habt. :-D


Was den vierten Fall angeht, hat Huo recht: Man kann irgendwelche Anker nutzen, die kein Zeichen matchen aber am Zeilenanfang zutreffen, wie z.B. Wortanfangsanker (die wir noch nicht hatten und die strenggenommen auch nicht zu EREs gehoeren auch wenn egrep sie versteht):

Code: Alles auswählen

\<^Zeilenanfang
Use ed once in a while!

Huo
Beiträge: 614
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Huo » 04.06.2022 18:30:08

Meillo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 17:07:49
Die Klammern sind was ich gemeint habe, aber es geht sogar noch einfacher: Man macht einfach (unnoetige) Klammern ganz aussen um die ganze RE rum. Da muss man dann gar nichts denken. ;-)

Code: Alles auswählen

(^irgendeine RE)
Ha, auf die naheliegendste Lösung sind wir alle nicht gekommen. :lol:

Die nur fünf Aufgaben dieser Kurseinheit schärfen, finde ich, das grundlegende Verständnis für die Struktur von REs ungemein, obwohl (oder gerade weil) drei von ihnen nicht praxisrelevant erscheinen :THX:. Oder gibt es praktische Anwendungssituationen, in denen man eine tautologische bzw. kontradiktorische RE braucht, die grundsätzlich immer bzw. nie matcht? Oder eine RE mit einem Circumflex-Anker, der nicht am Anfang steht?

Um die gelernten Anker auch mal praktisch einzusetzen, habe ich mir einen sed-Befehl gebastelt, der in meinen Lösungspostings die Original-Aufgabenstellungen in Zitat-Tags einschließt (also alle Zeilen, die mit 1), 2), 3) etc. beginnen).

Code: Alles auswählen

sed -E 's/^[[:digit:]]+\).+$/[quote]&[\/quote]/' regex_aufg_07.txt
Das "&" im Ersetzungsteil steht bei sed für das gematchte Suchmuster. Funktioniert natürlich nur, solange die Darstellung der Aufgabenzählung sich nicht verändert und die Aufgaben jeweils nur aus einer Zeile bestehen. :wink:

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

Re: RegExp-Kurs 07: Anker, Praezedenz, Zusammenfassung

Beitrag von Meillo » 04.06.2022 23:06:53

Huo hat geschrieben: ↑ zum Beitrag ↑
04.06.2022 18:30:08
Die nur fünf Aufgaben dieser Kurseinheit schärfen, finde ich, das grundlegende Verständnis für die Struktur von REs ungemein, obwohl (oder gerade weil) drei von ihnen nicht praxisrelevant erscheinen :THX:. Oder gibt es praktische Anwendungssituationen, in denen man eine tautologische bzw. kontradiktorische RE braucht, die grundsätzlich immer bzw. nie matcht? Oder eine RE mit einem Circumflex-Anker, der nicht am Anfang steht?
Falls das Filtern in einem Script optional sein soll und die Filter-RE per Parameter uebergeben wird, dann kann man eine immer matchende RE als Default verwenden:

Code: Alles auswählen

re="${1:-^}"
some-command ... | egrep "$re" | ...
(Das ist wie wenn ein Filter-Kommando frei waehlbar ist, dann kann man `cat' als Default-Kommando verwenden, das nichts filtert.)

Aber das sind eher seltene Faelle. Insgesamt sind diese Szenarien schon theoretisch gewesen.


Grundsaetzliches lernt man bei so abstrakten Aufgaben natuerlich eher, aber man muss dort disziplinierter denken und das braucht genug Motivation. Auf praxisrelevante und anschauliche Aufgaben haben die meisten mehr Lust. Darueber habe ich allerdings davor nicht nachgedacht. Ich habe die Aufgaben jeweils aus dem Bauch gestellt. Zum Glueck habe ich immer wieder mal einen guten Punkt erwischt. Die guten Aufgaben waren all diejenigen ueber die ihr diskutiert habt ... und das waren oft diejenigen, in denen ihr noch etwas gefunden habt, an das ich gar nicht gedacht hatte. ;-) Das beste an der Sache ist, dass ich dabei auch Verschiedenes gelernt, anders betrachtet oder besser verstanden habe.

Es gibt noch ein paar aeltere Aufgaben und Posts auf die ich noch reagieren wollte ... Mal schauen wie ich die Zeit dazu finde ... :roll:
Use ed once in a while!

Antworten