Personal Backup Vers. 4 und 5

© 2010, J. Rathlev, IEAP, Uni-Kiel

Reguläre Ausdrücke mit Personal Backup

Leider ist die Homepage der Delphi-Bibliothek für Reguläre Ausfrücke (TRegExp) von Andrey V. Sorokin und damit auch die dort früher bereitgestellten Informationen nicht mehr erreichbar. Daher habe ich überarbeitete deutsche Übersetzung der originalen Hilfe-Datei in die Dokumentation von Personal Backup eingefügt.

Im Programm können reguläre Ausdrücke für die Filterung von Ordnernamen, Dateinamen und Erweiterungen verwendet werden. Dabei gibt es unterschiedliche Voreinstellungen:

Mit dem Modifikator i kann diese Einstellung bei Bedarf angepasst werden.

Außerdem gibt es Unterschiede zwischen den Versionen 4 und 5. Personal Backup 5 verwendet TRegExp in der Unicode-Version

Einführung

Reguläre Ausdrücke sind eine weitverbreitete Methode, um Textmuster zu beschreiben, nach denen gesucht werden soll. Spezielle Metazeichen erlauben das Definieren von Bedingungen, beispielsweise soll ein bestimmter gesuchter String am Anfang oder am Ende einer Zeile vorkommen, oder ein bestimmtes Zeichen soll n-mal Vorkommen.

Reguläre Ausdrücke sehen üblicherweise für Anfänger ziemlich kryptisch aus, sind aber im Grunde genommen recht einfache (wenn man das Prinzip verstanden hat), aber enorm mächtige Werkzeuge.

Einfache Treffer

Jedes einzelne Zeichen findet sich selbst, außer es sei ein Metazeichen mit einer speziellen Bedeutung (siehe weiter unten).

Eine Sequenz von Zeichen findet genau diese Sequenz im zu durchsuchenden String (Zielstring). Also findet das Muster (= reguläre Ausdruck) "bluh" genau die Sequenz "bluh" irgendwo im Zielstring. Soweit ist das noch ganz einfach.

Damit man Zeichen, die üblicherweise als Metazeichen oder Escape-Sequenzen dienen, als ganz normale Zeichen ohne jede Bedeutung finden kann, stelle man solch einem Zeichen ein "\" voran. Diese Techninik nennt man Escaping. Ein Beispiel: das Metazeichen "^" findet den Anfang des Zielstrings, aber "\^" findet das Zeichen "^" (Circumflex), "\\" findet also "\" etc.

Beispiele:
foobar      findet den String 'foobar'
\^FooBarPtr findet den String '^FooBarPtr'

Escape-Sequenzen

Zeichen können auch mittels einer Escape-Sequenz angeben werden. Die Syntax ähnelt derer, wie sie in C oder Perl verwendet wird: "\n" findet eine neue Zeile, "\t" einen Tabulator etc.
Für beliebige ASCII-Zeichen gibt man "\xnn" an, wobei nn ein String aus hexadezimalen Ziffern ist. Es wird das Zeichen gefunden, dessen ASCII Code gleich nn ist. Bei Unicode-Zeichen (16 Bit kodierte Zeichen) benutzt man '\x{nnnn}', wobei 'nnnn' – eine oder mehrere hexadezimale Ziffern sind.

CodeBeschreibungÄquivalenz
\tTabulator (HT/TAB)\x09
\nZeilenvorschub (LF)\x0a
\rWagenrücklauf (CR)\x0d
\fSeitenvorschub (FF)\x0c
\aAlarm (bell) (BEL)\x07
\eEscape (ESC)\x1b
\xnnZeichen mit dem Hex-Code nn (ASCII-Text)
\x{nnnn}Zeichen mit dem Hex-Code nnnn (ein Byte für ASCII-Text und zwei Bytes für Unicode-Zeichen
Beispiele:
foo\x20bar findet 'foo bar' (beachte das Leerzeichen in der Mitte)
\tfoobar   findet 'foobar', dem unmittelbar ein Tabulator vorangeht

Zeichenklassen

Man kann sogenannte Zeichenklassen definieren, indem man eine Liste von Zeichen, eingeschlossen in eckige Klammern [], angibt. Solch eine Zeichenklasse findet genau eines der aufgelisteten Zeichen im Zielstring.

Falls das erste aufgelistete Zeichen, das direkt nach dem "[", ein "^" ist, findet die Zeichenklasse jalle Zeichen ausser denjenigen in der Liste.

Beispiele:
foob[aeiou]r  findet die Strings 'foobar', 'foober' etc. aber nicht 'foobbr', 'foobcr' etc.
foob[^aeiou]r findet die Strings 'foobbr', 'foobcr' etc. aber nicht 'foobar', 'foober' etc.

Innerhalb der Liste kann das Zeichen "-" benutzt werden, um einen Bereich oder eine Menge von Zeichen zu definieren. So definiert a-z alle Zeichen zwischen "a" and "z" inklusive.

Falls das Zeichen "-" selbst ein Mitglied der Zeichenklasse sein soll, dann setz man es als erstes oder letztes Zeichen in die Liste oder schützt es mit einem vorangestellten "\" (escaping). Wenn das Zeichen "]" ebenfalls Mitglied der Zeichenklasse sein soll, dann setzt man es als erstes Zeichen in die Liste oder als "\]".

Beispiele:
[-az]     findet 'a', 'z' und '-'
[az-]     findet 'a', 'z' und '-'
[a\-z]    findet 'a', 'z' und '-'
[a-z]     findet alle 26 Kleinbuchstaben von 'a' bis 'z'
[\n-\x0D] findet eines der Zeichen #10, #11, #12 oder #13
[\d-t]    findet irgendeine Ziffer, '-' oder 't'
[]-a]     findet irgendein Zeichen von ']'..'a'

Metazeichen

Metazeichen sind Zeichen mit speziellen Bedeutungen. Sie sind die Essenz der regulären Ausdrücke. Es gibt verschiedene Arten von Metazeichen wie unten beschrieben.

Metazeichen - Zeilenseparatoren

CodeBeschreibung
^Zeilenanfang
$Zeileende
\ATextanfang
\ZTextende
.ein beliebiges Zeichen
Beispiele:
^foobar  findet den String 'foobar' nur, wenn es am Zeilenanfang vorkommt
foobar$  findet den String 'foobar' nur, wenn es am Zeilenende vorkommt
^foobar$ findet den String 'foobar' nur, wenn er der einzige String in der Zeile ist
foob.r   findet Strings wie 'foobar', 'foobbr', 'foob1r' etc.

Standardmässig garantiert das Metazeichen "^" nur, dass das Suchmuster sich am Anfang des Zielstrings befinden muss, oder am Ende des Zielstrings mit dem Metazeichen "$". Kommen im Zielstring Zeilenseparatoren vor, so werden diese von "^" oder "$" nicht gefunden.

Man kann allerdings den Zielstring als mehrzeiligen Puffer behandeln, so dass "^" die Stelle unmittelbar nach, und "$" die Stelle unmittelbar vor irgendeinem Zeilenseparator findet. Diese Art der Suche kann mit dem Modifikator /m eingestellt werden.

\A und \Z funktionieren genau so wie "^" und "$", außer dass sie keine mehrfachen Stellen finden, wenn der Modifikator /m verwendet wird. "^" and "$" dagegen finden die zu suchenden Zeichen an jedem internen Zeilentrenner.

Das "." Metazeichen findet standardmässig alle Zeichen, also auch Zeilenseparatoren. Dies kann mit Auschalten des Modifikators /s verhindert werden.

TRegExpr geht mit Zeilenseparatoren so um, wie es auf www.unicode.org (http://www.unicode.org/unicode/reports/tr18/) empfohlen ist:

"^" ist am Anfang des Eingabestrings, und, falls der Modifikator /m gesetzt ist, auch unmitelbar folgend einem Vorkommen von \x0D\x0A oder \x0A oder \x0D (falls die Unicode-Version von TRegExpr verwendet wird, dann auch nach \x2028 oder \x2029 oder \x0B oder \x0C oder \x85). Zu beachten ist, dass es bei der Sequenz \x0D\x0A keine leere Zeile gibt. Diese beiden Zeichen werden als zusammengehörig behandelt.

"$" ist am Anfang des Eingabestrings, und, falls der Modifikator /m gesetzt ist, auch unmitelbar vor einem Vorkommen von \x0D\x0A oder \x0A or \x0D (falls die Unicode-Version von TRegExpr verwendet wird, dann auch vor \x2028 oder \x2029 oder \x0B oder \x0C oder \x85). Zu beachten ist, dass es bei der Sequenz \x0D\x0A keine leere Zeile gibt. Diese beiden Zeichen werden als zusammengehörig behandelt.

"." findet ein beliebiges Zeichen. Wenn der Modifikator /s ausgeschaltet ist, findet "." allerdings keine Zeilenseparatoren \x0D\x0A und \x0A und \x0D mehr (falls die Unicode-Version von TRegExpr verwendet wird, dann auch \x2028 und \x2029 und \x0B und \x0C and \x85).

Zu beachten ist, dass "^.*$" (was auch eine leere Zeile finden sollte) dennoch nicht den leeren String innerhalb der Sequenz \x0D\x0A findet, wohl aber den Leerstring innerhalb der Sequenz \x0A\x0D.

Die Behandlung des Zielstrings als mehrzeiliger String kann mittels der TRegExpr-Eigenschaften LineSeparators und LinePairedSeparator leicht den erfordelichen Bedürfnissen angepasst werden. Man kann entweder nur den UNIX-Stil- Zeilenseparator \n benutzen, nur die DOS-Stil-Separatoren \r\n oder beide gleichzeitig (wie schon oben beschrieben und wie es als Standard gesetzt ist). Es ist auch möglich, eigene Zeilenseparatoren zudefinieren.

Metazeichen – vordefinierte Klassen

\wein alphanumerisches Zeichen inklusive "_"
\Wkein alphanumerisches Zeichen, auch kein "_"
\dein numerisches Zeichen
\Dkein numerisches Zeichen
\sirgendein wörtertrennendes Zeichen (entspricht [ \t\n\r\f])
\Skein wörtertrennendes Zeichen

\w, \d und \s dürfen auch innerhalb von selbst definierten Zeichenklassen benutzt werden.

Beispiele:
foob\dr     findet Strings wie 'foob1r', "foob6r' etc., aber not 'foobar', 'foobbr' etc.
foob[\w\s]r findet Strings wie 'foobar', 'foob r', 'foobbr' etc., aber nicht 'foob1r', 'foob=r' etc.

TRegExpr benutzt die Eigenschaften SpaceChars und WordChars, um die Zeichenklassen \w, \W, \s, \S zu definieren. Somit lassen diese sich auch leicht umdefinieren.

Metazeichen – Wortgrenzen

\bfindet eine Wortgrenze
\Bfindet alles ausser einer Wortgrenze

Eine Wortgrenze (\b) is der Ort zwischen zwei Zeichen, welcher ein \w auf der einen und ein \W (s.o.) auf der anderen Seite hat bzw. umgekehrt. \b bezeichnet alle Zeichen des \w bis vor das erste Zeichen des \W bzw. umgekehrt.

Metazeichen - Iteratoren

Jedem Teil eines regulären Ausdruckes kann eine spezielle Art von Metazeichen folgen, ein Iterator. Dank dieser Metazeichen kann die Häufigkeit des Auftretens des Suchmusters im Zielstring definiert werden. Dies gilt jeweils für das vor diesem Metazeichen stehende Zeichen, Metazeichen oder den Teilausdruck.

{n}genau n-maliges Vorkommen ("gierig")
{n,}mindestens n-maliges Vorkommen ("gierig")
{n,m}mindestens n-, aber höchstens m-maliges Vorkommen ("gierig")
*kein- oder mehrmaliges Vorkommen ("gierig"), wie {0,}
+ein- oder mehrmaliges Vorkommen ("gierig"), wie {1,}
?kein- oder einmaliges Vorkommen ("gierig"), wie {0,1}
{n}?genau n-maliges Vorkommen ("genügsam")
{n,}? mindestens n-maliges Vorkommen ("genügsam")
{n,m}?mindestens n-, aber höchstens m-maliges Vorkommen ("genügsam")
*?kein- oder mehrmaliges Vorkommen ("genügsam"), wie {0,}?
+?ein oder mehrmaliges Vorkommen ("genügsam"), wie {1,}?
??kein- oder einmaliges Vorkommen ("genügsam"), wie {0,1}?

Die Ziffern in den geschweiften Klammern in der Form {n,m} geben an, wieviele Male das Suchmuster im Zielstring gefunden muss, um einen Treffer zu ergeben. Die Angabe {n} ist gleichbedeutend wie {n,n} und findet genau n Vorkommen. Die Form {n,} findet n oder mehre Vorkommen. Es gibt keine Beschränkungen für die Zahlen n und m. Aber je grösser sie sind, desto mehr Speicher und Zeit wird benötigt, um den regulären Ausdruck auszuwerten.

Falls eine geschweifte Klammer in einem anderen als dem eben vorgestellten Kontext vorkommt, wird sie wie ein normales Zeichen behandelt.

Beispiele:
foob.*r    findet Strings wie 'foobar', 'foobalkjdflkj9r' und 'foobr'
foob.+r    findet Strings wie 'foobar', 'foobalkjdflkj9r', aber nicht 'foobr'
foob.?r    findet Strings wie 'foobar', 'foobbr' und 'foobr', aber nicht 'foobalkj9r'
fooba{2}r  findet den String 'foobaar'
fooba{2,}r findet Strings wie 'foobaar', 'foobaaar', 'foobaaaar' etc.
fooba{2,3} findet Strings wie 'foobaar', or 'foobaaar', aber nicht 'foobaaaar'

Eine kleine Erklärung zum Thema "gierig" oder "genügsam":
"Gierig" nimmt soviel wie möglich vom Suchmuster, wohingegen "genügsam" mit so wenig wie nötig bereits zufrieden ist.
Beispiel: 'b+' und 'b*' angewandt auf den Zielstring 'abbbbc' findet 'bbbb', 'b+?' findet 'b', 'b*?' findet den leeren String, 'b{2,3}?' findet 'bb', 'b{2,3}' findet 'bbb'.

Alle Iteratoren können mit dem Modifikator /g auf den genügsamen Modus umgeschaltet werden.

Metazeichen - Alternativen

Es gibt eine Reihe von Alternativen für ein Suchmuster, die mit einem "|" voneinander getrennt werden. Auf diese Art findet das Suchmuster "fee|fie|foe" eines von "fee", "fie", oder "foe" im Zielstring. Das Gleiche würde auch mit f(e|i|o)e erreicht.

Die erste Alternative beinhaltet alle Zeichen vom letzten Muster-Begrenzer ("(", "[" oder natürlich der Anfang des Suchmusters) bis zum ersten "|". Die letzte Alternative beinhaltet alles vom letzten "|" bis zum nächsten Muster-Begrenzer. Aus diesem Grunde ist es zu empfehlen, die Alternativen immer in Klammern anzugeben, um möglichen Missverständnissen darüber vorzubeugen, wo die Alternativen beginnen und enden.

Alternativen werden von links nach rechts geprüft, so dass der Treffer im Zielstring aus den jeweils zuerst passenden Alternativen zusammengesetzt ist. Das bedeutet, dass Alternativen nicht notwendigerweise "gierig" sind. Ein Beispiel: Wenn man mit "(foo|foot)" im Zielstring "barefoot" sucht, so passt bereits die erste Variante. Diese Tatsache mag nicht besonders wichtig erscheinen, aber es ist natürlich wichtig, wenn der gefundene Text weiterverwendet wird. Im Beispiel zuvor würde der Benutzer nicht "foot" erhalten, wie er eventuell beabsichtigt hatte, sondern nur "foo".

Man sollte dabei nicht vergessen, dass das "|" innerhalb von eckigen Klammern wie ein normales Zeichen behandelt wird, so dass [fee|fie|foe] dasselbe bedeutet wie [feio|].

Beispiel:
foo(bar|foo) findet die Strings 'foobar' oder 'foofoo'

Metazeichen - Teilausdrücke

Das Klammernkonstrukt (...) wird auch dazu benutzt, reguläre Teilausdrücke zu definieren (nach dem Abnalysieren findet man Positionen, Längen und effektive Inhalte der regulären Teilausdrücke in den TRegExpr-Eigenschaften MatchPos, MatchLen und Match und kann die Schablonen-Ausdrücke in TRegExpr.Substitute durch sie ersetzen).

Teilausdrücke werden von links nach rechts nummeriert, jeweils in der Reihenfolge ihrer öffnenden Klammer. Der erste Teilausdruck hat die Nummer 1. Der gesamte reguläre Ausdruck hat die Nummer 0 und wird in der Ersetzen-Schablone (TRegExpr.Substitute) durch '$0' oder '$&' repräsentiert).

Beispiele:
(foobar){8,10}  findet Strings, die 8, 9 oder 10 Vorkommen von 'foobar' beinhalten
foob([0-9]|a+)r findet 'foob0r', 'foob1r' , 'foobar', 'foobaar', 'foobaar' etc.

Metazeichen - Rückwärtsreferenzen

Die Metazeichen \1 bis \9 werden in Suchmustern als Rückwärtsreferenzen interpretiert. \<n> findet einen zuvor bereits gefundenen Teilausdruck #<n>.

Beispiele:
(.)\1+         findet 'aaaa' und 'cc'.
(.+)\1+        findet auch 'abab' und '123123'
(['"]?)(\d+)\1 findet "13" (innerhalb "), oder '4' (innerhalb ') oder auch 77, etc.

Modifikatoren

Modifikatoren sind dazu da, das Verhalten von TRegExpr zu verändern.

Es gibt viele Wege, die weiter unten beschriebenen Modifikatoren zu nutzen. Jeder der Modifikatoren kann in das Suchmuster des regulären Ausdruckes mittels des Konstruktes (?...) eingebettet werden.

Die meisten Modifikatoren können aber auch durch Setzen der entsprechenden TRegExpr-Eigenschaft auf den gewünschten Wert beeinflusst werden (Beispiel: Zuweisung an ModifierX oder ModifierStr für alle Modifikatoren zugleich).

Die Standardwerte für neue Instanzen von TRegExpr-Objekte sind definiert in globalen Variablen. Beispielsweise definiert die globale Variable RegExprModifierX das Verhalten des Modifikators X und damit die Einstellung der TRegExpr-Eigenschaft ModifierX bei neu instantiierten TRegExpr-Objekten.

(?i)

Die Suche erfolgt ohne Berücksichtigung der Groß- und Kleinschreibung (ev. abhängig von den Sprach-Einstellungen des Betriebssystems).

(?-i)

Die Suche erfolgt mit Berücksichtigung der Groß- und Kleinschreibung.

(?m)

Behandle den Zielstring als mehrzeiligen String, d.h., dass die Bedeutungen von "^" und "$" sich ändern (s.o).:
Statt nur den Anfang oder das Ende des Zielstrings zu finden, wird jeder Zeilenseparator innerhalb eines Strings erkannt (siehe auch Zeilenseparatoren)

(?s)

Behandle den Zielstring als einzelne Zeile., d.h., dass "." jedes beliebige Zeichen findet, auch Zeilenseparatoren), die es normalerweise nicht findet.

(?-g)

Dieser (nicht-standard) Modifikator schaltet alle folgenden Operatoren in den "Genügsam"-Modus (standardmassig sind alle Operatoren "gierig" ($g)). Danach arbeitet '+' wie '+?', '*' als '*?' etc.

(?r)

Dieser (nicht-standard) Modifikator schaltet in einen speziellen Modus für russische Symbole (normalerweise ausgeschaltet).

(?x)

Erweitert die Lesbarkeit des Suchmusters durch Zwischenräume und Kommentare. Er bewirkt, dass alle Zwischenräume, die nicht durch eine Escape-Sequenz oder innerhalb einer Zeichenklasse gekennzeichnet sind, ignoriert werden. Man kann damit den regulären Ausdruck in kleinere, besser lesbare Teile zerlegen. Das Zeichen # wird als Metazeichen behandelt und leitet einen Kommentar bis zum Zeilenende ein.

Beispiel:
(
(abc) # Kommentar 1
  |   # man kann Zwischenräume zur Formatierung benutzen - TRegExpr ignoriert sie
(efg) # Kommentar 2
)

Wenn man also echte Zwischenräume oder das # im Suchmuster haben will, (ausserhalb einer Zeichenklasse, wo sie durch /x nicht ignoriert werden), muss das entweder als Escape-Sequenz oder int der hexadezimalen Schreibweise angegeben werden. Beides zusammen sorgt dafür, dass reguläre Ausdrücke besser lesbar werden.

Beispiele:
(?i)Sankt-Petersburg      findet 'Sankt-petersburg' und 'Sankt-Petersburg'
(?i)Sankt-(?-i)Petersburg findet 'Sankt-Petersburg', aber nicht 'Sankt-petersburg'
(?i)(Sankt-)?Petersburg   findet 'Sankt-petersburg' und 'sankt-petersburg'
((?i)Sankt-)?Petersburg   findet 'sankt-Petersburg', aber nicht 'sankt-petersburg'

Sonstiges

(?#text)

Ein Kommentar, der Text wird ignoriert. Beachte, dass TRegExpr den Kommentar abschliesst, sobald er eine ")" sieht. Es gibt also keine Möglichkeit, das Zeichen ")" im Kommentar zu haben.


J. Rathlev, D-24098 Kiel, Jan. 2010