RegExp-Kurs 05: Zeichenklassen

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

RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 08.05.2022 11:15:39

Kursuebersicht


Teil 05: Zeichenklassen


Zeichenklassen kann man sich so aehnlich wie Alternationen vorstellen, bloss fuer einzelne Zeichen statt fuer ganze Ausdruecke. Von der Ausdrucksmoeglichkeit sind sie theoretisch nicht unbedingt noetig wenn man Alternation und Unterausdruecke hat, aber in der Praxis sind sie ungeheuer praktisch. Ausserdem sind sie ein Standardfeature, das man in in allen RE-Varianten identisch wiederfindet.

Wenn wir nach ``Samstag'' oder ``Sonnabend'' suchen wollen, dann brauchen wir dafuer eine Alternation: `Samstag|Sonnabend'.

Wenn wir nach ``Maier'' oder ``Meier'' suchen wollten, dann koennen wir das ebenfalls mit einer Alternation machen: `Maier|Meier'. Hier ist die Abweichung der zwei Varianten jedoch nur gering. Den gemeinsamen Teil koennen wir darum auch nur einmal schreiben und einen Unterausdruck nutzen: `M(a|e)ier'.

Wenn nun alle Teilausdruecke der Alternation nur genau ein Zeichen sind, dann koennen wir stattdessen auch eine Zeichenklasse verwenden:

Code: Alles auswählen

M[ae]ier
Eine Zeichenklasse steht immer fuer genau ein einziges Zeichen. Dieses kann ein beliebiges der aufgelisteten Zeichen sein.

Eine Zeichenklasse wird mit eckigen Klammern geschrieben, zwischen denen alle Zeichen aufgefuehrt werden fuer die die Zeichenklasse stehen kann. In diesem Beispiel kann die Zeichenklasse also entweder fuer `a' oder fuer `e' stehen.


Metazeichen

Innerhalb von Zeichenklassen werden fast alle Zeichen, also auch die normalen Metazeichen (wie Pipe und Klammern und auch das Escapezeichen Backslash!), literal interpretiert.

Speziell sind in Zeichenklassen nur wenige Zeichen, auf die ich nun nach und nach komme.

Die schliessende eckige Klammer (]). Diese markiert das Ende der Zeichenklasse. Wenn die schliessende eckige Klammer von der Zeichenklasse literal gematcht werden soll, dann muss sie als erstes Zeichen verwendet werden:

Code: Alles auswählen

[]abc]
Diese Zeichenklasse steht fuer eines der vier Zeichen: schliessende eckige Klammer, `a', `b' oder `c'.

Die schliessende eckige Klammer hat also an jeder Stelle einer Zeichenklasse eine Metabedeutung (naemlich die Zeichenklasse zu beenden), ausser als erstes Zeichen innerhalb der Zeichenklasse.


Negation

Zeichenklassen koennen eines was Alternationen nicht koennen, naemlich den Match invertieren. Eine Zeichenklasse kann man ausdruecken: Jedes Zeichen *ausser* den angegebenen. Dazu muss als erstes Zeichen in der Zeichenklasse ein Circumflex (Caret) stehen. Dieser negiert die Zeichenklasse. Die Zeichenklasse matcht dann alle Zeichen ausser die auf den Circumflex folgenden.

Eine Zeichenklasse, die alle Zeichen, ausser den Satzzeichen Punkt, Ausrufezeichen und Fragezeichen matcht sieht folgendermassen aus:

Code: Alles auswählen

[^.!?]
Der Circumflex ist also auch ein Zeichen, das innerhalb einer Zeichenklasse ein Metazeichen ist, allerdings nur wenn er als erstes Zeichen in der Zeichenklasse kommt. An jeder anderen Stelle steht er literal fuer sich selbst.

Diese Zeichenklasse matcht auf die Satzzeichen und den Circumflex:

Code: Alles auswählen

[.!?^]
Diese ebenfalls:

Code: Alles auswählen

[.^!?]

Bereiche

Eine Zeichenklasse, die eine beliebige Ziffer matcht kann man noch einigermassen hinschreiben:

Code: Alles auswählen

[0123456789]
Aber bei einer Zeichenklasse, die einen beliebigen Buchstaben matcht, wird das sehr unpraktisch. Darum gibt es hierfuer die Moeglichkeit, Bereiche mittels eines Minuszeichens anzugeben:

Code: Alles auswählen

[a-z]
Da die Reihenfolge der Zeichen vom Zeichensatz abhaengt, sollte man dies nur auf US-ASCII anwenden ... genauer gesagt, sind eigentlich nur drei Bereiche (und Teile davon) einigermassen verlaesslich:

1) 0-9 fuer die Ziffern
2) A-Z fuer die Grossbuchstaben
3) a-z fuer die Kleinbuchstaben

Wobei bei den Buchstaben nicht unbedingt eindeutig ist, ob Umlaute enthalten sein werden oder nicht. Solange man sich nur im Rahmen von US-ASCII bewegt, klappt das alles gut, darueber hinaus sollte man nicht zu genaue Erwartungen haben. Diese Ungenauigkeiten kann man in der Praxis meist problemlos in Kauf nehmen, angesichts der praktischen Bequemlichkeit von Bereichen in Zeichenklassen.

Will man sowohl Gross- als auch Kleinbuchstaben matchen, so muss man beide Bereiche separat angeben:

Code: Alles auswählen

[A-Za-z]
Dies steht fuer ein Zeichen, das ein beliebiger Gross- oder Kleinbuchstabe ist.

Ein Minuszeichen bildet immer dann einen Bereich, wenn es in einer Zeichenklasse zwischen zwei literalen Zeichen steht. In diesen Faellen ist es ein Metazeichen. Steht das Minuszeichen ganz am Anfang oder ganz am Ende, so kann es keinen Bereich aufspannen und steht dann literal fuer sich selbst.


Vordefinierte Bereiche

Da die Internationalisierung Zeichenklassenbereiche erschwert hat und weil man immer wieder bestimmte Bereiche braucht, die man ggf. auch nicht schnell mal hinschreiben kann (wie alle druckbaren Zeichen, beispielsweise), hat POSIX einige Zeichenklassenausdruecke eingefuehrt:

[:alnum:] alphanumerische Zeichen
[:alpha:] Buchstaben
[:blank:] Leerzeichen und Tab
[:cntrl:] Steuerzeichen
[:digit:] Ziffern
[:graph:] druckbare Zeichen ausser Whitespace
[:lower:] Kleinbuchstaben
[:print:] alle druckbaren Zeichen
[:punct:] Punktuationszeichen, Klammern, etc.
[:space:] Whitespace
[:upper:] Grossbuchstaben
[:xdigit:] Hexadezimalziffern

Jeder davon steht fuer eine Liste von Zeichen bzw. einen Bereich. So steht, vereinfacht gesagt, `[:lower:]' fuer `a-z'. Man achte hier auf die eckigen Klammern! Eine Zeichenklasse, die die Kleinbuchstaben matcht kann so aussehen:

Code: Alles auswählen

[a-z]
oder so:

Code: Alles auswählen

[[:lower:]]   
Eine Zeichenklasse, die sowohl Kleinbuchstaben als auch Ziffern matcht kann eine der folgenden Formen haben:

Code: Alles auswählen

[a-z0-9]
[[:lower:][:digit:]]
[[:lower:]0-9]
[01234[:lower:]5-9]
usw.
Zeichenklassen sind Mengen, d.h. die Reihenfolge ihrer Elemente ist egal.


POSIX beschreibt noch weitere Spezialangaben fuer innerhalb von Zeichenklassen ([= =] und [. .]), aber die werden seltenst gebraucht und hier darum ausgelassen.


Reihenfolge

Bei der Reihenfolge innerhalb der Zeichenklasse sind die drei Zeichen mit Sonderbedeutung ^ - ] relevant. Der Circumflex hat nur an wirklich allererster Stelle seine Sonderbedeutung. Die schliessende eckige Klammer verliert ihre Sonderbedeutung als erstes Zeichen auch noch hinter dem Circumflex. Wenn also beides auftritt, dann muss der Circumflex zur Negation zuerst kommen. Das Minus verliert seine Sonderbedeutung als letztes Zeichen (wo es auch mit keinem anderen Sonderzeichen kollidieren kann). Oder, falls keine schliessende eckige Klammer vorhanden ist, als erstes Zeichen (ggf. nach einem Negations-Circumflex).



Aufgaben:

Schreibe Regulaere Ausdruecke, wenn moeglich, dann gerne verschiedene Varianten. Setzte sie mit egrep um und teste sie gegen selbst ausgedachten Input.

1) RE fuer eine Hexadezimalziffer.

2) RE fuer eine dreistellige Ganzzahl

3) RE fuer eine dreistellige Ganzzahl mit (Pflicht-)Vorzeichen.

4) RE fuer einen Euro-und-Cent-Betrag kleiner 10,-.

5) RE fuer die 31 Tage des Januars (zweistelliges Format mit fuehrender Null). Zwei Varianten.

6) RE fuer die 28 Tage des Februars (zweistelliges Format mit fuehrender Null). Zwei Varianten.

7) RE fuer ein beliebiges Zeichen, das kein Buchstabe ist.

8) RE fuer eine beliebige Ziffer. Drei verschiedene Varianten.

9) RE fuer ein Zeichen, das kein Circumflex (^) ist.

10) RE fuer eine Zeichenklasse, die nur einen Circumflex matcht.

11) RE fuer ein Minus oder eine eckige Klammer.

12) RE die alles ausser einer schliessenden eckigen Klammer oder einem Circumflex matcht.

13) Schreibe zwei verschiedene REs, die beide einen Backslash matchen.

14) Was matcht die Zeichenklasse `[A-z]' ausser Buchstaben sonst noch? (Zeichensatz: US-ASCII)

15) RE, die einen Tab matcht. Versuche mehrere Varianten zu finden.

16) Schreibe einen egrep-Ausdruck (nun auch mit Zeichenklassen), um die Schreibweisen Maier, Meier, Mayer, Meier und Myer zu matchen.
Use ed once in a while!

Benutzeravatar
TRex
Moderator
Beiträge: 8325
Registriert: 23.11.2006 12:23:54
Wohnort: KA

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von TRex » 08.05.2022 22:12:08

Zusatzaufgabe: Welcher Fehler ist meillo bei 8​) unterlaufen? :P (fällt eher unter das Gebiet der ersten Übungen)
Jesus saves. Buddha does incremental backups.
Windows ist doof, Linux funktioniert nichtDon't break debian!Wie man widerspricht

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 08.05.2022 22:31:23

TRex hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 22:12:08
Zusatzaufgabe: Welcher Fehler ist meillo bei 8​) unterlaufen? :P (fällt eher unter das Gebiet der ersten Übungen)
Also ich finde so ein 8) anstelle von 8⁣) irgendwie angemessen, falls Du das meinst. Bezeichnet halt eine besonders coole Aufgabe.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 09.05.2022 08:05:20

TRex hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 22:12:08
Zusatzaufgabe: Welcher Fehler ist meillo bei 8​) unterlaufen? :P (fällt eher unter das Gebiet der ersten Übungen)
Ach, euch reichen die 16 Aufgaben noch nicht? :-P Kein Problem, nach deiner 17. Aufgabe habe ich hier noch:

18) Schreibe eine RE, die den String ``8​)'' matcht. ;-) (Das ist eine ernst gemeinte Aufgabe und ist ist mit den bisher gelernten RE-Moeglichkeiten umsetzbar.)
Use ed once in a while!

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von tegula » 11.05.2022 00:01:30

Hab meinen Lösungsversuch ins NoPaste hochladen (NoPaste-Eintrag41679).

Edit (11.05): Aufgabenstellung und Rechtschreibung des NoPaste-Uploads korrigiert und anschließend Link im Forums-Post erneuert.
Zuletzt geändert von tegula am 11.05.2022 10:11:45, insgesamt 3-mal geändert.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 11.05.2022 01:05:29

tegula hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 00:01:30
Hab meinen Lösungsversuch ins NoPaste hochladen (NoPaste-Eintrag41677).
Wie immer der Erste! :-D

Auf den ersten Blick ist mir aufgefallen, dass du RE-Features verwendest, die wir noch nicht behandelt haben. Das Ziel sollte sein, die Aufgaben jeweils nur mit den schon vorgestellten Features zu loesen. Auch wenn manches mit noch nicht durchgenommenen Features bequemer gemacht werden kann, so ist es doch auch fuer Personen, die REs bereits koennen, gut, sich auf die Einschraenkung einzulassen. Das festigt die Wissensbasis.
Use ed once in a while!

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 11.05.2022 09:54:33

Mir haben die Aufgaben diesmal viel Kopfzerbrechen, aber auch viel Spaß bereitet! :)
Meillo hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 11:15:39
1) RE fuer eine Hexadezimalziffer.

Code: Alles auswählen

[[:xdigit:]]
[1-9A-Fa-f]
2) RE fuer eine dreistellige Ganzzahl

Code: Alles auswählen

[1-9][0-9][0-9]
[1-9][[:digit:]][[:digit:]]
3) RE fuer eine dreistellige Ganzzahl mit (Pflicht-)Vorzeichen.

Code: Alles auswählen

[+-][1-9][0-9][0-9]
[-+][1-9][0-9][0-9]
4) RE fuer einen Euro-und-Cent-Betrag kleiner 10,-.

Code: Alles auswählen

[[:digit:]],[[:digit:]][[:digit:]]
[0-9],[0-9][0-9]
5) RE fuer die 31 Tage des Januars (zweistelliges Format mit fuehrender Null). Zwei Varianten.

Code: Alles auswählen

(0[1-9]|[1-2][0-9]|3[0-1])
([1-3]0|[0-2][1-9]|31)
6) RE fuer die 28 Tage des Februars (zweistelliges Format mit fuehrender Null). Zwei Varianten.

Code: Alles auswählen

(0[1-9]|1[0-9]|2[0-8])
(0[1-9]|[1-2][0-8]|19)
7) RE fuer ein beliebiges Zeichen, das kein Buchstabe ist.

Code: Alles auswählen

[^A-Za-z]
[^[:alpha:]]
... oder falls zusätzlich auch Leerzeichen nicht gematcht werden sollen:

Code: Alles auswählen

[^A-Za-z ]
[^[:alpha:][:blank:]]
8) RE fuer eine beliebige Ziffer. Drei verschiedene Varianten.

Code: Alles auswählen

[0-9]
[[:digit:]]
[0123456789]
9) RE fuer ein Zeichen, das kein Circumflex (^) ist.
10) RE fuer eine Zeichenklasse, die nur einen Circumflex matcht.
Aber eigentlich braucht man dafür keine Zeichenklasse:
11) RE fuer ein Minus oder eine eckige Klammer.

Code: Alles auswählen

[][-]
12) RE die alles ausser einer schliessenden eckigen Klammer oder einem Circumflex matcht.

Code: Alles auswählen

[^]^]
13) Schreibe zwei verschiedene REs, die beide einen Backslash matchen.

Code: Alles auswählen

\\
[\]
14) Was matcht die Zeichenklasse `[A-z]' ausser Buchstaben sonst noch? (Zeichensatz: US-ASCII)
Die Manpage(7) für ASCII enthält eine Tabelle aller ASCII-Zeichen, kann also für diese Aufgabe genutzt werden:

Code: Alles auswählen

$ man 7 ascii 2> /dev/null | egrep -o '[A-z]' | egrep '[^[:alpha:]]' | sort -u
[
\
]
^
_
`
15) RE, die einen Tab matcht. Versuche mehrere Varianten zu finden.

Code: Alles auswählen

$ egrep '       ' re5.txt     # literales Tab im Terminal eingeben mit Tasten Ctrl+V und Tab
Tab	und Ende.

Code: Alles auswählen

$ egrep '[[:blank:]]' re5.txt | egrep [[:cntrl:]]
Tab	und Ende.
Letzteres funktioniert wohl, da ein Tab anders als ein Leerzeichen auch zu den Steuerzeichen zählt. Edit: Hab das aber nur mit Umleitung (doppelter egrep-Anwendung) hinbekommen. Ginge das auch in einer RE? Dafür bräuchte man wohl analog zur Alternation (|, Oder-Verknüpfung) eine Konjunktion (Und-Verknüpfung) ...
16) Schreibe einen egrep-Ausdruck (nun auch mit Zeichenklassen), um die Schreibweisen Maier, Meier, Mayer, Meier und Myer zu matchen.

Code: Alles auswählen

egrep 'M([ae][iy]|y)er'

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 11.05.2022 11:34:21

@Huo:

Hast du deine Loesung fuer (10) gut getestet?

Gibt es fuer (11) und (12) weitere Loesungsmoeglichkeiten?

Die Aufgabe (18) nicht vergessen. Die war durchaus ernst gemeint und bietet Erkenntnisgewinnpotenzial, auch bzgl. REs. ;-)
Use ed once in a while!

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 11.05.2022 14:11:12

Meillo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 11:34:21
Hast du deine Loesung fuer (10) gut getestet?
Huch, nach ausgiebigerem Testen sehe ich, dass die Zeichenklasse [\^] auch den Backslash "\" matcht, also keine korrekte Lösung ist.
Gibt es fuer (11) und (12) weitere Loesungsmoeglichkeiten?
Stimmt, vor lauter Zeichenklassen habe ich die Alternation vergessen ... :wink:
Aufgabe (11) – RE fuer ein Minus oder eine eckige Klammer – mit Alternation:

Code: Alles auswählen

(-|\[|\])
Bei Aufgabe (12) – RE für alles außer einer schließenden eckigen Klammer oder einem Circumflex – muss ich passen, da macht mir die erforderliche Negation der Alternation Probleme. Mit egrep bekomme ich es nur mit Option "-v" hin, was sicherlich nicht gemeint war:

Code: Alles auswählen

egrep -v '(\^|\])' re5.txt
18) Schreibe eine RE, die den String ``8​)'' matcht. ;-)

Code: Alles auswählen

$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep '8)'
$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep '8​)'
Schreibe eine RE, die den String 8​)'' matcht. ;-)
$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep $'8\u200b)'
Schreibe eine RE, die den String 8​)'' matcht. ;-)
Letztere Lösung nutzt ANSI-C Quoting, was mich auch auf eine weitere Lösung für Aufgabe (15) bringt:

Code: Alles auswählen

$ egrep $'\t'  re5.txt 
Tab	und Ende.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 11.05.2022 14:40:00

Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:11:12
Meillo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 11:34:21
Hast du deine Loesung fuer (10) gut getestet?
Huch, nach ausgiebigerem Testen sehe ich, dass die Zeichenklasse [\^] auch den Backslash "\" matcht, also keine korrekte Lösung ist.
Genau.

Bei manchen Aufgaben geht es weniger darum, einfach nur eine Loesung hinzuknallen, sondern darum, laut nachzudenken und Gedankenwege aufzuzeigen, auch wenn sie nicht zur funktionierenden Loesung fuehren. Im Dialog kommt man dann vielleicht gemeinsam weiter.

Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:11:12
Gibt es fuer (11) und (12) weitere Loesungsmoeglichkeiten?
Stimmt, vor lauter Zeichenklassen habe ich die Alternation vergessen ... :wink:
Aufgabe (11) – RE fuer ein Minus oder eine eckige Klammer – mit Alternation:

Code: Alles auswählen

(-|\[|\])
Bei Aufgabe (12) – RE für alles außer einer schließenden eckigen Klammer oder einem Circumflex – muss ich passen, da macht mir die erforderliche Negation der Alternation Probleme. Mit egrep bekomme ich es nur mit Option "-v" hin, was sicherlich nicht gemeint war:

Code: Alles auswählen

egrep -v '(\^|\])' re5.txt
Und ohne Alternation, sondern die Zeichenklasse anders geschrieben? Also z.B. `[cba]' statt `[abc]'.

Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:11:12
18) Schreibe eine RE, die den String ``8​)'' matcht. ;-)

Code: Alles auswählen

$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep '8)'
$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep '8​)'
Schreibe eine RE, die den String 8​)'' matcht. ;-)
$ echo "Schreibe eine RE, die den String ``8​)'' matcht. ;-)" | egrep $'8\u200b)'
Schreibe eine RE, die den String 8​)'' matcht. ;-)
Letztere Lösung nutzt ANSI-C Quoting, was mich auch auf eine weitere Lösung für Aufgabe (15) bringt:

Code: Alles auswählen

$ egrep $'\t'  re5.txt 
Tab	und Ende.
Das ist wieder sehr interessant hier: Was daran verarbeitet die Shell und was bekommt egrep wirklich zu sehen? (Manchmal hilft dabei `set -x'. Sonst hilft dabei natuerlich Denken und es mit Farben auf Papier zu schreiben ... Verarbeitungsschritte und Umformungen wie Rechenschritte in der Mathematik.)

Wie koennte man den String mit dem unsichtbaren Zeichen mit einer RE matchen, ohne Shellmagie und ohne das unsichtbare Zeichen literal hinzuschreiben?
Use ed once in a while!

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 11.05.2022 16:08:42

Meillo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:40:00
Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:11:12
Bei Aufgabe (12) – RE für alles außer einer schließenden eckigen Klammer oder einem Circumflex – muss ich passen, da macht mir die erforderliche Negation der Alternation Probleme. Mit egrep bekomme ich es nur mit Option "-v" hin, was sicherlich nicht gemeint war:

Code: Alles auswählen

egrep -v '(\^|\])' re5.txt
Und ohne Alternation, sondern die Zeichenklasse anders geschrieben? Also z.B. `[cba]' statt `[abc]'.
Also, ich finde keine alternative Schreibweise für die Zeichenklasse "[^]^]", jedenfalls nicht mit einer anderen Reihenfolge der Zeichen.

Begründung:
a) Der Circumflex als Steuerzeichen für die Negation muss an erster Stelle stehen.
b) Es gilt aber auch:
Meillo hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 11:15:39
Wenn die schliessende eckige Klammer von der Zeichenklasse literal gematcht werden soll, dann muss sie als erstes Zeichen verwendet werden
Das heißt, die literale eckige Klammer muss ebenfalls an erster Stelle stehen – in diesem Fall direkt hinter dem Negationszeichen "^". Für das literale "^" bleibt ergo nur die letzte Stelle.

Aber vielleicht übersehe ich etwas, und ein anderer Kursteilnehmer kann eine zweite Lösung zu Aufgabe (12) beitragen (bevor Meillo die Lösung verrät)? :wink:

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 12.05.2022 15:06:58

Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 16:08:42
Also, ich finde keine alternative Schreibweise für die Zeichenklasse "[^]^]", jedenfalls nicht mit einer anderen Reihenfolge der Zeichen.
Okay, unter der Voraussetzung, dass der Zeichensatz sich auf US-ASCII beschränkt, kann ich noch diese "abenteuerliche" Alternativlösung für Aufgabe (12) anbieten :? :

Code: Alles auswählen

[ -\_-~]
Dies sollte funktionieren, weil die beiden auszuschließenden Zeichen "]" und "^" in der ASCII-Tabelle direkt aufeinander folgen. Alle (druckbaren) Zeichen davor und dahinter habe ich jeweils als Bereich in die Zeichenklasse eingefügt.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von TuxPeter » 12.05.2022 16:57:05

Nunja, ich habe mich auch durch die Aufgaben gegrept und fand sie sehr schön aufeinander aufbauend, auch gar nicht besonders schwer.
Hat Spaß gemacht!
Habe meist die Alternation verwendet, weil mir das so schön gradlinig erschien - auch wo es, wie die Lösungen zeigen, andere und auch kürzere Vorschläge gab. (Mit leichtem Brainfuck-Effekt)

Für die 12) habe ich auch keine Lösung; und ich nehme an, dass Meillo noch was eleganteres präsentiert als die vorgeschlagenen.

Teils gibt es leichte Irritationen im Verständnis des Wörtchens "oder", weil das in der Umgangssprache anders funktioniert als in der strengen Logik. Schon klar, "alle Zeichen außer A oder B" meint "alle Zeichen, außer A und B". Da habe ich mich dann auch etwas aufs Glatteis führen lassen. Die Alternation entspricht ja einem ODER, mit entsrechender NEGATION müsste sich dann doch auch das UND darstellen lassen. Habe aber jetzt grade genug gefummelt für meinen Bedarf.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 12.05.2022 18:07:45

TuxPeter hat geschrieben: ↑ zum Beitrag ↑
12.05.2022 16:57:05
(Mit leichtem Brainfuck-Effekt)
Echte Brainfuck-Aufgaben waren für mich die Aufgaben (5) und (6), die mich irgendwie nicht losgelassen haben. :wink:
Jetzt habe ich für die Januar- und Februartage noch Lösungen gefunden, die jeweils mit einer einfachen statt einer doppelten Alternation auskommen:
Meillo hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 11:15:39
5) RE fuer die 31 Tage des Januars (zweistelliges Format mit fuehrender Null).
([0-2][1-9]|[1-3][0-1])
6) RE fuer die 28 Tage des Februars (zweistelliges Format mit fuehrender Null).
([0-1][1-9]|[1-2][0-8])

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von TuxPeter » 12.05.2022 19:59:30

Habe nun doch noch mal etwas herumgefummelt, mit mehr Testdaten, und da waren ein paar Klammern zu wenig - nun geht es aber:
Daten (die Underscores zwecks besserer Übersicht):

Code: Alles auswählen

_03.01_
_00.01_
_09.01_
_03,01_
_10.01_
_19.01_
_22.01_
_30.01_31.01_

Code: Alles auswählen

egrep -n -o "((0[1-9])|([12][0-9])|(3[01]))\.01" k5.txt
10:03.01
12:09.01
14:10.01
15:19.01
16:22.01
17:30.01
17:31.01
Deine Lösung ist aber eleganter und kürzer:

Code: Alles auswählen

egrep -n -o "([0-2][1-9]|[1-3][0-1])\.01" k5.txt
und tut es mit meinen Testdaten genauso gut. (Ich habe den Monat dazu genommen, das ist aber trivial - außer man vergisst, den Punkt zu escapen, dann kommen z.B. auch Beträge mit Komma)

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 12.05.2022 20:50:05

@TuxPeter: Deine Lösung stößt mich darauf, dass meine Lösungen jeweils ein überflüssiges Minuszeichen enthalten, was sie mich noch kürzer formulieren lässt. :wink:

Code: Alles auswählen

([0-2][1-9]|[1-3][01])
Übrigens bin ich auf meine finalen Lösungen erst durch eine tabellarische Visualisierung der 31 bzw. 28 Wochentage gekommen. Leider kann ich meine farbige Zeichnung derzeit wohl nicht als Bild hochladen? Deshalb hier als ASCII-Art am Beispiel des Januar:

Code: Alles auswählen

  0  1  2  3
 ___________
     _______
    |0  0  0|
  _ |_____  | -> [1-3][01]
 |1 |1  1 |1|              \
 |  |_____|_|               \
 |2  2  2 |                 / ([0-2][1-9]|[1-3][01])
 |.  .  . |                /
 |.  .  . | ---> [0-2][1-9]
 |9  9  9 |
 |________|

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 12.05.2022 20:54:38

Ich muss noch auf verschiedene Dinge in der Diskussion eingehen. An dieser Stelle mal zu den Kalendertagen:

Schoen, dass ihr das im Dialog und mit Abstand und neuen Ideen angeht. Genau so habe ich mir das gedacht.

Ihr merkt, dass REs kein Verstaendnis von Zahlreihen haben. Was in einer Programmiersprache total einfach ist (i >= 1 && i <=31), ist mit REs ganz schoen umstaendlich.

Interessanterweise wird die Aufgabe ganz einfach loesbar, wenn ihr sie grafisch betrachtet! Schreibt euch die Tage in eine Tabelle, mit Spalten fuer die Einerzahlen und Zeilen fuer die Zehnerzahlen. In dieser Flaeche koennen RE-Zeichenklassen Rechtecke abdecken. Ihr muesst die Flaeche lediglich mit Rechtecken abdecken, die dann problemlos in Zeichenklassen uebersetzt werden koennen. (Ueberlappungen sind kein Problem.)

Wie so oft ist die Problembetrachtungsweise entscheidend. Wenn man eine clevere Betrachtungsweise waehlt, dann ist die Loesungsumsetzung trivial. ;-)

Probiert es mal aus! Malt euch eure bisherigen REs auf! Ihr werdet staunen ... aber nur wenn ihr es grafisch seht.


Edit: Und Huo postet genau das Gleiche zur gleichen Zeit. :THX:
Zuletzt geändert von Meillo am 12.05.2022 21:18:41, insgesamt 1-mal geändert.
Grund: Ueberlappungshinweis von Huo ergaenzt
Use ed once in a while!

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Huo » 12.05.2022 21:04:22

Meillo hat geschrieben: ↑ zum Beitrag ↑
12.05.2022 20:54:38
Probiert es mal aus! Malt euch eure bisherigen REs auf! Ihr werdet staunen ... aber nur wenn ihr es grafisch seht.
Exakt das war mein Aha-Erlebnis! Meinen ersten "kleinteiligeren" Lösungen fehlte der Mut zur Überlappung, visualisiert betrachtet bestanden sie aus kleineren, disjunkten Rechtecken.

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

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von Meillo » 12.05.2022 21:33:09

Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 16:08:42
Meillo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:40:00
Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 14:11:12
Bei Aufgabe (12) – RE für alles außer einer schließenden eckigen Klammer oder einem Circumflex – muss ich passen, da macht mir die erforderliche Negation der Alternation Probleme. Mit egrep bekomme ich es nur mit Option "-v" hin, was sicherlich nicht gemeint war:

Code: Alles auswählen

egrep -v '(\^|\])' re5.txt
Und ohne Alternation, sondern die Zeichenklasse anders geschrieben? Also z.B. `[cba]' statt `[abc]'.
Also, ich finde keine alternative Schreibweise für die Zeichenklasse "[^]^]", jedenfalls nicht mit einer anderen Reihenfolge der Zeichen.

Begründung:
a) Der Circumflex als Steuerzeichen für die Negation muss an erster Stelle stehen.
b) Es gilt aber auch:
Meillo hat geschrieben: ↑ zum Beitrag ↑
08.05.2022 11:15:39
Wenn die schliessende eckige Klammer von der Zeichenklasse literal gematcht werden soll, dann muss sie als erstes Zeichen verwendet werden
Das heißt, die literale eckige Klammer muss ebenfalls an erster Stelle stehen – in diesem Fall direkt hinter dem Negationszeichen "^". Für das literale "^" bleibt ergo nur die letzte Stelle.
Das war alles richtig gedacht und geschlussfolgert. Deine Folgerung, dass das die einzige Moeglichkeit ist, ist korrekt. ;-)
Huo hat geschrieben: ↑ zum Beitrag ↑
11.05.2022 16:08:42
Aber vielleicht übersehe ich etwas, und ein anderer Kursteilnehmer kann eine zweite Lösung zu Aufgabe (12) beitragen (bevor Meillo die Lösung verrät)? :wink:
Nein, der Meillo kommt nicht vorbei um noch eine Wunderloesung beizutragen, da es keine andere gibt. :-D

Zugegeben, ich habe da bewusst herausfordernd gefragt. Wichtig war, dass du erklaeren konntest *warum* keine andere Reihenfolge in der Zeichenklasse moeglich ist. Meine Aufgaben schreibe ich nicht fuer ein schulisches System bei dem ich korrekt und falsch abhaken kann, sondern sie sollen euch auf Probleme stossen, bei deren Bearbeitung ihr etwas lernt. Mir ist voellig egal, ob ihr die Aufgaben richtig oder falsch loest, wichtiger ist, ob ihr hinterher verstanden habt, warum eine falsche Loesung falsch und die richtige Loesung richtig ist.


Deine clevere, auf print-Zeichen in US-ASCII beschraenkte Idee, hat mich dann ueberrascht und beeindruckt. Ich hatte gar nicht so weit gedacht und weniger geknobelt als du. Mein Respekt!

Eine RE, die ich uebrigens oft verwende, ist:

Code: Alles auswählen

[^ -~]
Die matcht alle Druckzeichen in US-ASCII. Danach suche ich im Editor, um ggf. noch vorhandene Umlaute rauszuwerfen. :lol:


Bei Aufgabe 11 gibt es uebrigens auch keine andere Loesung als die, die Huo praesentiert hat.


Aufgabe 10 ist ... nunja :oops: ... fuer euch gar nicht loesbar. :-P ... aber diese Tatsache ist ja auch ganz interessant. Natuerlich und zum Glueck braucht man die Zeichenklasse fuer ein einziges Zeichen gar nicht, trotzdem ist es interessant, dass es genau ein Zeichen gibt, das nicht alleine in einer Zeichenklasse stehen kann.

Es gibt jedoch einen Trick, den ich euch nicht gezeigt habe: Zwischen [. und .] kann man Collating Symbols angeben. Damit kann man vermutlich sowas wie `ß' und `ss' gleich matchen oder vielleicht geht es auch nur fuer sowas wie ff- und fi-Ligaturen ... Ihr seht, hier hoert mein Wissen auf. Jedenfalls funktioniert die Zeichenklasse so:

Code: Alles auswählen

[[.^.]]
(Ich bin mir aber nicht sicher, ob diese Loesung korrekt ist, weil ich nicht komplett verstanden habe wie dieses Feature funktioniert. Falls es jemand weiss, erklaert es mir!)
Use ed once in a while!

eggy
Beiträge: 3334
Registriert: 10.05.2008 11:23:50

Re: RegExp-Kurs 05: Zeichenklassen

Beitrag von eggy » 13.05.2022 08:01:28

Was Du da machst: Du bastelst Dir ein Sonderzeichen, das nur aus dem Dach besteht, und dann sagst Du wähle das Dachsonderzeichen aus der Menge des armen einzelnen vereinsamtem Dachsonderzeiches.

Am deutlichsten wird das, glaube ich, wenn man erstmal realisiert hat, das Digraphen in anderen Sprachen viel präsenter als im Deutschen sind. Ich denke, der einzige deutschsprachige Digraph (auch wenn er per Definition keiner ist), dem man regelmäßig mehr oder weniger bewusst über den Weg läuft, ist das "sch", nämlich als eigenes Sortierfach oder Kategorie in Telefonbuch etc.
Gut nachvollziehbar hier geschildert: https://en.wikipedia.org/wiki/Ch_(digraph)#Czech

Mit der Kombination von [. und .] Zeichen sagst Du, alle Zeichen in der Mitte, also bei "[.sch.]" dann "sch", werden als ein zusammenhängender Buchstabe betrachtet. Ist in der theoretischen Betrachtung von Automaten sehr viel interessanter, weil da je nach Automatenform sehr strikt "ein Übergang = ein konsumiertes Zeichen" gilt, d.h., um "Schuh" zu verarbeiten braucht Dein Automat dann entweder drei oder fünf Zustände, je nachdem ob Du Trigraphen erlaubst oder nicht.

Die gawk manpage erklärt das auch ganz gut:
Collating Symbols
A collating symbol is a multi-character collating element enclosed in [. and .]. For example, if ch is a collating element, then [[.ch.]] is a regular expression that matches this collating element, while [ch] is a regular expression that matches either c or h.
und man 7 regex:
Within a bracket expression, a collating element (a character, 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 example, if the collating sequence includes a "ch" collating element, then the RE "[[.ch.]]*c" matches the first five characters of "chchcc".
Interessant wird das für die Praxis vielleicht erst in dem Moment, wo man sich eingehender mit sort beschäftigt:
https://pubs.opengroup.org/onlinepubs/9 ... 7_03_02_04
Wo wird ein "sch" einsortiert? vor "s", bei "scha", nach "s"?

Ich hab das [. .] bisher nie gebraucht. Für die Verarbeitung von DoubleWide Zeichen ist es möglicherweise auch noch von Interesse, aber da mach ich, soweit es geht, einen großen Bogen rum... mir scheint jedenfalls, das ist ein Artefakt einer prähistorischen Zeit, in der es noch kein UTF-8 gab.

Man bräuchte da wohl mal ne Übersicht, welches Programm bringt welche Form von Regeximplementierung von wann mit und wie geht das jeweilige Programm mit Sonderfällen wie Digraphen um (ich wette da findet man mal wieder besonders grusliges in den coreutils :mrgreen: ). Ich geh davon aus, dass das damals ein Ansatz war, um die "ein Zeichen"-Situation bei Doublewidechars herumzuhacken, vermutlich dann durch utf-8 obsolet geworden. Heute geht wohl alles über die Standardlibs? Was vermutlich auch der Grund ist, warum das in keinem von mir getestetem Programm funktioniert hat (oder zu den Tomaten haben sich noch größere Mengen falscher und fehlender Quotingzeichen gesellt, ka ist schon spät). Ich tippe auf Regression in der zugrunde liegenden Lib, siehe https://www.mail-archive.com/bug-grep@g ... 02653.html

Ne alternative Lösung wäre übrigens, falls die [. .] richtig funktionieren würden, möglicherweise die Posix symbolic names zu nutzen, ich nehme an, damit sollte der Reihenfolgenzwang ausgehebelt werden können, kann es aber grade nicht testen. Würde mich mal interessieren, was z.B. nen Solaris daraus macht.

Antworten