--- title: "Reguläre Ausdrücke - Wenn sie nur nicht so praktisch wären" date: 2019-09-26 draft: false tags: ["programmieren","helper","regex"] categories: ["Archive"] --- Ich habe lange einen Bogen um reguläre Ausdrücke gemacht. Jedes Mal wenn ich es mit dem Thema zu tun bekam war die instinktive Reaktion die Flucht. Ich habe mich immer mal wieder versucht diesem Thema zu nähern. Die ganzen schlauen Menschen, die reguläre Ausdrücke beherrschen, können ja nicht ganz falsch liegen. Ich habe allen Mut zusammen genommen und mich durchgekämpft. Ich muss ganz ehrlich zugeben ich bin immer noch nicht der allergrößte Fan regulärer Ausdrücke. Trotzdem, sie sparen viel Zeit. Das geht bei ganz einfachen Beispielen los. Stell Dir vor Du hast in Google Sheets eine lange Tabelle mit Artikelnummern. Die Artikelnummern haben eine einheitliche Struktur (z.B. artikel-farbe-12587). Was machst Du, wenn Du z.B. die Reihenfolge der Segmente ändern möchtest? Mit ein paar  einfachen Formeln und hier und da eine Hilsspalte lässt sich das lösen 😁. Mit einem regulären Ausdruck kann man dieses Problem  wesentlich eleganter lösen. Google Sheets besitzt eine Funktion _REGEXREPLACE()._ Damit kann man dieses Problem mit einer simplen Formel lösen. Bevor wir aber zur Lösung kommen müssen wir ein paar Grundlagen kennen. ## Die Grundlagen für reguläre Ausdrücke Reguläre Ausdrücke helfen  bei der Erkennung Mustern innerhalb Zeichenketten. Das kleinste Element einer Zeichenkette ist ein einzelnes Zeichen. Damit fangen wir an. Es gibt bei der Verwendung regulärer Ausdrücke verschiedene sog. Zeichenklassen: ### Zeichenklassen |RegEx | Bedeutung | |-------|-------------------------------------------------------------------| |. | Jedes beliebige Zeichen | |\d | Ziffern (0-9) | |\D | Alle Zeichen ausgenommen Ziffern | |\s | Leerzeichen (Leerzeichen, Tab, CR, LF | |\S | Alle Zeichen, die kein Leerzeichen sind | |\w | alphanumerische Zeichen inklusive | |\W | jedes Zeichen, dass kein alphanumerisches Zeichen inklusive | Damit können wir z.B. schon einmal ein Datumsmuster suchen: \d\d\.\d\d\.\d\d\d\d Zwei Ziffern, gefolgt einem Punkt, gefolgt 2 Ziffern, einem Punkt und 4 Ziffern.  Der Punkt bekommt hier einen \ vorangestellt. Dadurch wird klar gemacht, dass wir nicht _jedes beliebige Zeichen_ meinen, sondern tatsächlich den Punkt. Genauso funktioniert das auch bei den Ziffern. Wäre der \ nicht vor dem d würde nach dem Buchstaben d gesucht werden und nicht nach der Zeichenklasse. ## Besondere Zeichen Neben den geläufigen Zeichen wie Buchstaben, Zahlen und Satzzeichen gibt es Sonderzeichen. |Regex |Bedeutung| |-------|----------------| |c | z.B. der Buchstabe "c"| |^ | Zeilenanfang / Negation bei [^..] Zeichenklassen| |$ | Ende der Zeile oder Zeichenkette| |\\ | hebt die spezielle Bedeutung des nächsten Zeichens auf| |\n | LF Vorschub in die nächste Zeile / Zeilenumbruch| |\r | CR oder WR - Rückbewegung der Schreibbewegung auf Pos.1 der gleichen Zeile| |\r\n | Zeilenumbruch DOS / Windows| |\t | Tabulator| |\f | FF oder Seitenumbruch - Bewegung in die erste Zeile der nächsten Seite| |\a | Beep oder Piep| |\e | Escape| |\b | leere Zeichenkette am Wortanfang oder Wortende| |\B | leere Zeichenkette nicht am Anfang oder Ende des Wortes| |\< | leere Zeichenkette am Wortanfang| |> | leere Zeichenkette am Wortende| ### Eigene Zeichenklassen festlegen Man kann auch eigene Zeichenklassen festlegen: |Regex |Bedeutung| |-------|----------------| |[abc] | a, b, oder c - eine sog. einfache Klasse| |[^abc] |jedes Zeichen, dass nicht a, b, oder c ist| |[a-h]      |Zeichenbereich a bis h| |[a-h]'[r-u]| Zeichen im Bereich zwischen a bis h oder r bis u| In einer  Zeichenklasse werden entweder einzelne Zeichen definiert [aeiou] oder ein Bereich [a-h0-9]. Mit dem ^ kann man die Klasse negieren, d.h. [^abc] bedeutet jedes Zeichen, dass nicht ein a, b oder c ist. Außerdem kann man mehrere Zeichenklassen mit dem ' Operator (oder) verbinden. ## Angaben Mengen Man kann in regulären Ausdrücken angeben wie oft ein Zeichen darf. |Regex |Bedeutung| |-------|----------------| |a?   | einmal oder gar nicht| |a   | gar nicht bis beliebig oft| |a+   | einmal bis beliebig oft| |a{3} | genau dreimal| |a{3,5} | dreimal oder mehr, aber maximal fünfmal| Damit kann man z.B. Nummernblöcke suchen: \d{3}-\d{4}-\d{5} findet Nummern, die so formatiert sind: 123-4567-89101. ## Gierige und träge Quantifizierer Bei der Angabe Mengen in regulären Ausdrücken gibt es sogenannte träge und gefräßige Quantifizierer. Dabei versuchen  gierige Quantifizierer so viel wie möglich pro Ergebnis zu verarbeiten. Ihre trägen Kollegen wollen so wenig wie möglich pro Ergebnis verarbeiten . ### Träge Quantifizierer: |Regex |Bedeutung| |-------|----------------| |a?    | gar nicht bis so selten wie möglich| |a+?    |  einmal bis so selten wie möglich| a{3,}? |   dreimal oder mehr, aber so wenig wie möglich | ### Gierige Quantifizierer: |Regex |Bedeutung| |-------|----------------| |a+ |gar nicht bis so oft wie möglich | |a++ |einmal bis so so oft wie möglich | |a{3,}+ |dreimal oder mehr, aber so oft wie möglich| Nehmen wir einmal an wir haben den Satz Hallo -Bob-, wie geht es -Dir-? Und jetzt suchen wir einmal mit einem gierigen und einem trägen Quantifizierer: (gieriger Quantifizierer): -.- findet: -Bob-, wie geht es -Dir- (träger Quantifizierer): -.\?- findet: -Bob- -Dir- Man sieht, dass der gierige Quantifizierer im gleichen Satz einen langen Treffer findet. Der träge Quantifizierer findet stattdessen zwei kurze Treffer. ## Gruppen Man kann Teile eines regulären Ausdrucks einander abgrenzen. Dabei werden Gruppen in einem regulären Ausdruck durch Klammern definiert. Gruppen innerhalb eines regulären Ausdrucks erhalten eine Nummer. Die Nummerierung so, dass der komplette gefundene Ausdruck die 0 erhält. Danach werden die jeweiligen Gruppen hochgezählt werden. --- Beispiel Artikelnummer: artikel-rot-2545 regulärer Ausdruck: (\w+)-(\w+)-(\d+) --- Der reguläre Ausdruck findet die komplette Artikelnummer. Es werden aber auch 4 Nummern zugewiesen. Für den Abruf verwendet man $n also $ + der jeweiligen Nummer. In diesem Beispiel sieht das wie folgt aus: --- $0 = artikel-rot-2545 $1 = artikel $2 = rot $3 = 2545 --- Nun haben wir alles was wir für unser Ursprungsproblem brauchen. Die Formel in Google Sheets sieht wie folgt aus: --- =REGEXREPLACE(A1;"(\w+)-(\w+)-(\d+)";"$3-$2-$1") --- Beispiel: [http://Google Sheets](https://docs.google.com/spreadsheets/d/1jE5u8q2bKauiIQu6bGRX76Y-XE3CVtuumtwgRBis1Rw/edit?usp=sharing) Das gleiche Prinzip lässt sich in diversen Programmiersprachen anwenden. Dabei gib es leichte Unterschiede je nach Sprache: Python: re.sub(r'(\w+) (\w+)',r'\2 \1','Wort1 Wort2') Go: regex := regexp.MustCompile(\`(\w+) (\w+)\`) fmt.Printf(regex.ReplaceAllString("Wort1 Wort2", "$2 $1")) Javascript: let regex = /(\w+) (\w+)/; "Wort1 Wort2".replace(regex, "$2 $1"); alternativ auch so in Javascript: let regex2 = new RegExp("(\w+) (\w+)"); "Wort1 Wort2".replace(regex2, "$2 $1"); ## Lookaround Jetzt wird es etwas komplizierter 😇. Man kann auch den Kontext eines regulären Ausdrucks angeben. D.h. wir können ganz konkret nach etwas suchen, dass sich vor oder hinter einer bestimmten Zeichenfolge befindet. ### Look behind Beispiel Artikelliste: artikel-rot-2538 artikel-gelb-2539 artikel-blau-2542 artikel-lila-2543 artikel-rot-2545 artikel-gelb-2546 Regulärer Ausdruck + Look behind: --- (?<=artikel-)\w+ Der 1\. Ausdruck artikel- muss dem 2\. Ausdruck \w+ vorausgehen (?