Dieser Beitrag stammt ursprünglich von www.linuxfocus.org
Guido Socher Über den Author: Guido ist ein Linux-Fan, weil es ein freies Betriebssystem ist, und es einfach Spaß macht, mit Leuten der Linux Gemeinde aus aller Welt zusammenzuarbeiten. Seine Freizeit verbringt er mit seiner Freundin, hört BBC World Service Radio, macht Fahrradtouren und arbeitet mit Linux. E-Mail an den Author
|
Zusammenfassung: Regular Expressions werden zum Kontext abhängigen Suchen und
Editieren von Texten benutzt. Sie werden in guten Editoren, Text-Parsern und verschiedenen
Programmiersprachen benutzt.
Regular Expressions kommen in vielen Editoren wie vi, emacs in Programmen wie grep, egrep und in Programmiersprachen wie awk, perl und sed zur Anwendung.
Mit Regular Expressions kann kontextabhängig nach Textmustern gesucht werden. Eine Regular Expression ist deshalb nicht ein einfacher Suchstring, sondern die formale Beschreibung eines Textmusters.
Als ich das erste Mal sah, wie man mit Hilfe von Regular Expressions Texte editieren kann, war ich einfach begeistert. Änderungen an einem Text, für die ich Stunden gebraucht hätte, waren in wenigen Sekunden erledigt. Was ich jedoch auf dem Bildschirm sah, erschien mir wie eine zufällige Ansammlung von Punkten, Strichen und anderen Zeichen. Trotzdem, eines war mir klar: Ich wollte die Sprache der Regular Expressions lernen und war schon bald sehr erstaunt, wie einfach sie zu verstehen ist. Sie folgt wenigen einfachen Syntaxregeln.
Obwohl Regular Expressions in der UNIX Welt so weit verbreitet sind, gibt es nicht die eine Regular Expressions Sprache. Es sind vielmehr eine Anzahl verschiedener Dialekte, die man hier vorfindet. Es gibt zum Beispiel zwei Vertreter des bekannten grep Programmes, egrep und grep. Perl kann man wohl in diesem Zusammenhang als die Sprache mit der am weitesten entwickelten Regular Expressions Syntax bezeichnen. Zum Glück folgen alle diese Dialekte denselben einfachen Prinzipien und sobald man diese verstanden hat, ist der Rest sehr einfach.
In diesem Artikel möchte ich mich auf die Grundlagen beschränken, die Details kann man dann in den man-pages nachlesen.
Angenommen wir hätten folgende Telefonliste einer Firma:
Phone Name ID ... ... 3412 Bob 123 3834 Jonny 333 1248 Kate 634 1423 Tony 567 2567 Peter 435 3567 Alice 535 1548 Kerry 534 ...
Es handelt sich um eine Firma mit 500 Leuten und die Daten sind einfach in einem normalen ASCII File gespeichert. Einträge von Personen, die mit einer 1 starten arbeiten in Gebäude 1. Wer arbeitet in Gebäude 1?
Eine Regular Expression kann das beantworten: grep '^1' phonelist.txt oder egrep '^1' phonelist.txt oder perl -ne 'print if (/^1/)' phonelist.txt
In Worten bedeutet das: Suche nach allen Zeilen, die mit einer 1 anfangen. Das Zeichen "^" sorgt dafür, daß nur nach Einsen am Anfang der Zeilen gesucht wird.
Das Grundelement einer Regular Expression ist das Ein-Zeichen Muster. Die mit den Ein-Zeichen Mustern ist nur erfolgreich, wenn genau dieses eine Zeichen im Text zu finden ist. Ein Beispiel für ein Ein-Zeichen Muster ist die 1 im Beispiel von oben.
Ein anderes Beispiel für ein Ein-Zeichen Muster ist: egrep 'Kerry' phonelist.txt
Dieses Muster besteht nur aus Ein-Zeichen Mustern (den Buchstaben K,e usw.)
Mehrere Zeichen kann man in einer Menge zusammenfassen. Die Menge wird durch eckige Klammern angezeigt. Die gesamte Menge ist als ganzes auch ein Ein-Zeichen Muster. Eine Suche mit solch einer Menge ist erfolgreich, wenn genau eines der Zeichen aus der Menge im Textstring vorhanden ist. Ein Beispiel:
[abc] Ein Ein-Zeichen Muster mit dem man nach genau einem der Zeichen a, b oder c sucht. [ab0-9] Ein Ein-Zeichen Muster mit dem man nach a oder b oder einer Zahl aus dem ASCII Bereich von Null bin Neun sucht [a-zA-Z0-9\-] Das sucht nach Klein- oder Groß- buchstaben oder einer Zahl oder dem Minuszeichen.
Angewendet auf die Telefonliste:
egrep '^1[348]' phonelist.txt
Dieser Ausdruck sucht nach Zeilen, die mit 13, 14 oder 18 anfangen.
Die meisten ASCII Zeichen führen zur erfolgreichen Suche, wenn genau das Zeichen selbst im Text steht, aber es gibt auch Regular Expression Zeichen, die eine spezielle Bedeutung haben. Die rechteckigen Klammern starten z.B die Definition einer Menge. In einer Menge ist "-" ein spezielles Regular Expression Zeichen und gibt einen Bereich im ASCII Zeichensatz an. Um die normale Bedeutung eines solchen speziellen Zeichens zu bekommen, stellt man einfach einen Backslash "\" vor. In einigen Dialekten der Regular Expression Sprache haben wiederum der Backslash in Verbindung mit einem anderem Zeichen eine spezielle Bedeutung. In diesem Fall erhält man die normale Bedeutung in dem man den Backslash entfernt.
Der Punkt ist auch ein wichtiges Regular Expression Zeichen. Die Suche ist erfolgreich, wenn das verglichene Zeichen ein anderes Zeichen als das ASCII new-line Zeichen (new-line = neue Zeile) ist. Beispiel:
grep '^.2' phonelist.txt
Dieser Ausdruck sucht nach Zeilen mit einer 2 in der zweiten Position und irgend einem Zeichen an erster Stelle.
Mengen kann man invertieren indem man die Definition mit "[^" an Stelle von "[" startet. Nun bedeutet das "^" nicht mehr Anfang der Zeile sondern, "[" und "^" zusammen bezeichnen die inverse Menge.
[0-9] Ein Ein-Zeichen Muster, die Suche ist erfogreich, wenn das Zeichen im Text eine Zahl ist. [^0-9] Alles was keine Zahl ist. [^abc] Alles was kein a, b oder c ist. . Alles bis auf das new-line Zeichen. Identisch mit [^\n]. Das \n ist das new-line Zeichen. Um nach allen Zeilen zu suchen, die NICHT mit einer Eins anfangen schreibet man: egrep '^[^1]' phonelist.txt
Vorher haben wir gesehen, daß "^" genau dem Anfang einer Zeile entspricht. Anchors sind spezielle Regular Expression Zeichen, die nicht ein Zeichen, sondern eine Position bezeichnen.
^ Der Zeilenanfang $ Das Zeilenende
Um nach Leuten mit einer company-ID von 567 in unserer Liste zu suchen, benutzt man einfach den Ausdruck:
egrep '567$' phonelist.txt
In Worten: Suche nach zeilen, die mit 567 enden.
Multiplier Zeichen geben an, wie oft ein Ein-Zeichen Muster im Text vorkommen soll:
Beschreibung | grep | egrep | perl | vi | vim | vile | elvis | emacs |
---|---|---|---|---|---|---|---|---|
nie oder oft | * | * | * | * | * | * | * | * |
mindestens ein mal | \{1,\} | + | + | \+ | \+ | \+ | + | |
vielleicht ein mal | \? | ? | ? | \= | \? | \= | ? | |
n bis m mal | \{n,m\} | {n,m} | \{n,m\} |
Anmerkung: Bei den verschiedenen VI Typen sollten die Option magic gesetzt sein.
Ein Beispiel aus der Telefonliste:
.... 1248 Kate 634 .... 1548 Kerry 534 ....
Um nach einer Zeile zu suchen die sich aus einer 1, einigen Zahlen, einigen Leerzeichen und dem Buchstaben K zusammensetzt, tippt man:
grep '^1[0-9]\{1,\} \{1,\}K' phonelist.txt oder man benutzt * und wiederholt [0-9] und das Leerzeichen: grep '^1[0-9][0-9]* *K' phonelist.txt oder egrep '^1[0-9]+ +K' phonelist.txt oder perl -ne 'print if (/^1[0-9]+ +K/)' phonelist.txt
Die Multiplier Zeichenfolge bestimmt, wie oft das vorhergehende Ein-Zeichen Muster im Text vorkommen soll. "23*4" bedeutet z.B. nicht "2, eine 3, vielleicht irgend etwas und dann eine 4" (das wäre "23.*4")! Vielmehr bedeutet "23*4" daß nach "einer 2, vielleicht einigen Dreien und einer 4" gesucht werden soll.
Es ist wichtig zu wissen, daß diese Multiplier gierig sind. Das bedeutet, daß das erste Multiplier Suchmuster sich so weit wie möglich nach rechts ausdehnt.
Dieser Sachverhalt macht keinen großen Unterschied für grep, ist aber beim Editieren von Texten wichtig.
Der Ausdruck ^1.*4 würde sich über ganze Zeile 1548 Kerry 534 von Anfang Ende ausdehnen. Der Ausdruck dehnt sich nicht nur auf den kleinsten Bereich, also 154, aus, sondern so weit es geht.
Das Klammern als Speicherkonstrukt beeinflußt nicht die Art oder die Zeichen nach denen gesucht wird. Es sorgt nur dafür, daß ein Textstring abgespeichert und später über Variablen darauf zugegriffen werden.
Das erste Klammern als Speicherkonstrukt wird über Variable 1 angesprochen, das zweite über Variable 2 und so weiter.
Programm Name | Syntax | Syntax der Variablen |
---|---|---|
grep | \(\) | \1 |
egrep | () | \1 |
perl | () | \1 or ${1} |
vi,vim,vile,elvis | \(\) | \1 |
emacs | \(\) | \1 |
Ein Beispiel:
Der Ausdruck [a-z][a-z] sucht nach Kleinbuchstaben.
Nun kann man diese Variablen benutzen, um nach Textmustern wie 'otto' zu suchen:
egrep '([a-z])([a-z])\2\1' Die Variable \1 enthält den Buchstaben o und \2 den Buchstaben t Der Ausdruck würde auch auf anna passen, aber nicht auf yxyx.
Klammern als Speicherkonstrukte werden normalerweise nicht zum Suchen von Namen wie otto und anna benutzt, sondern zum Editieren sowie zum Suchen und Ersetzen.
Zum Editieren von Texten braucht man einen Editor wie vi oder emacs oder Perl All diese Programme beherrschen Regular Expressions.
In emacs benutzt man M-x query-replace-regexp oder replace-regexp. M-x query-replace-regexp läßt sich jedes Suchen und Ersetzen bestätigen. Am besten legt man sich query-replace-regexp oder replace-regexp auf eine Funktionstaste.
Im vi benutzt man :%s/ / /gc. Das % bezeichnet die Ex-region 'ganze datei' und kann natürlich durch jede andere Ex-Region ersetzt werden. Im Editor vim kann man zum Beispiel mit shift-v und Cursor-up/down eine Region einfach markieren. Leider würde eine Einführung in vim den Rahmen diese Artikel sprengen. / / /gc ist interaktiv und fragt nach einer Bestätigung. s/ / /g ist nicht interaktiv. In perl benutzt man:
perl -pe 's/ / /g'
Nun einige Beispiele. In der Firma müssen die Telefonnummern geändert werden. Alle Telefonnummern, die mit einer 1 anfangen, sollen nun eine 2 nach der zweiten Ziffer bekommen. Aus 1423 wird damit 14223.
Phone Name ID ... 3412 Bob 123 3834 Jonny 333 1248 Kate 634 1423 Tony 567 2567 Peter 435 3567 Alice 535 1548 Kerry 534 ...
und so einfach geht das:
vi: s/^\(1.\)/\12/g emacs: ^\(1.\) replaced by \12 perl: perl -pe 's/^(1.)/${1}2/g' phonelist.txt Nun sieht die Telefonliste so aus: Phone Name ID ... 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Kerry 534 ...
Perl kennt nicht nur die Variablen \1 \9. \12 würde deshalb auf Variable 12 verweisen. Um das Problem zu lösen benutzt man einfach ${1}.
Nur stehen die Spalten jetzt nicht mehr schön untereinander. Wie kann man das lösen? Man könnte z.B einfach testen, ob an der 5ten Position ein Leerzeichen steht.
vi: s/^\(....\) /\1 /g emacs: '^\(....\) ' replaced by '\1 ' perl: perl -pe 's/^(....) /${1} /g' phonelist.txt Jetzt hat man: Phone Name ID ... 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Karry 534 ...
Ein Kollege hat die phonelist.txt editiert und dabei nicht aufgepaßt. Am Anfang einiger Zeilen stehen nun einige Leerzeichen. Wie kann man das lösen ?
Phone Name ID ... 3412 Bob 123 3834 Jonny 333 12248 Kate 634 14223 Tony 567 2567 Peter 435 3567 Alice 535 15248 Kerry 534 ... Das sollte die Sache korrigieren: vi: s/^ *// (Vor dem Stern sind zwei Leerzeichen) emacs: '^ +' replaced by: Nichts perl: perl -pe 's/^ +//' phonelist.txt
Du schreibst gerade an einem Programm benutzt die Variablen temp und temporary. Nun möchtest Du temp durch die Variable counter ersetzen. Würde man einfaches Suchen und Ersetzen benutzen, so würde aus temporary, counterorary. Das willst Du vermeiden.
Regular Expressions machen das ganz einfach. Man ersetzt temp([^o]) mit counter\1. Das heißt, temp und nicht "o" wird ersetzt. (Eine andere Alternative wären Boundaries, die hier aber nicht besprochen wurden.)
Ich hoffe, daß dieser Artikel Dein Interesse geweckt hat. Nun solltest Du einfach mal in die man-pages Deines Lieblingseditors sehen.
Es gibt außerdem noch mehr spezielle Regular Expressions Zeichen wie z.B Alterations, eine Art Oder, und natürlich die oben erwähnten Boundaries.
Viel Spaß.
Guido
Webpages mantained by Miguel
Ángel Sepúlveda © Guido Socher 1998 LinuxFocus 1998 |