 |
Willkommen im TP Hilfe Forum unter Traum-Projekt.com
Aktuell bist Du in unseren Foren als Gast mit reinen Leserechten unterwegs. Wenn Du Dich registrierst, kannst Du eigene Themen verfassen und Fragen stellen, privat mit anderen TPlern kommunizieren, an Umfragen teilnehmen und gratis Fotos runterladen. Weitere Foren werden zugänglich, und Du wirst – falls gewünscht – per Mail über neue Beiträge informiert. Wir würden uns freuen, Dich in einer der freundlichsten Communitys als Mitglied begrüßen zu dürfen. Die Registrierung ist schnell und kostenlos. Sollten bei der Registrierung Fragen auftauchen reicht ein Klick in unsere Hilfe - Häufig gestellte Fragen oder eine kurze Mitteilung an das Support Team.
Viel Spaß bei Traum-Projekt.com
|
24.05.2003, 16:12
|
#1
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
[Workshop] Regular Expressions
Da immer häufiger Fragen zu regular Expressions gestellt werden - bzw solche, die darauf hinauslaufen -, habe ich mich entschlossen, hier einen kleinen Workshop zum Thema zu starten. Vorab aber noch eine Bemerkung:
Auch wenn mich hier jemand mal mit "regexp-Gott" tituliert hat : Alles weiß ich nicht und ich bin nicht unfehlbar! Wenn mir also Fehler unterlaufen sollten, habt Nachsicht mit mir...
Also, auf geht's...
Basics
Regular Expressions - oder, auf Deutsch, reguläre Ausdrücke - dienen dazu, einen Text auf bestimmte Zeichenfolgen zu überprüfen und diese dann - bei Bedarf - zu manipulieren. Diese Suchbegriffe können durch die regexp-Syntax sehr variabel gehalten werden.
Sie sind Programmiersprachen-unabhängig, soll heissen, sowohl für php als auch für Perl ( als Beispiel ) gelten die gleichen Regeln - nur das "Drumherum" ist anders. Deswegen werde ich mich - zu Beginn - nur mit den reinen regexp beschäftigen.
Regexp setzen sich zusammen aus Literalen und aus Metazeichen. Literale sind Buchstaben, auf die exakt geprüft werden soll und die Metazeichen bringen die Variablität rein. Ein Beispiel: Die regexp /abc/ besteht nur aus Literalen: sie schlägt nur bei abc zu, Abc zB wird ignoriert, da das große A nicht dem kleinen a entspricht.
Die - für uns vorerst wichtigen - Metazeichen sind:
1.Allgemein: - ^ steht für den Textanfang. /^abc/ ist nur dann wahr, wenn der Text mit abc beginnt.
- $ steht für das Textende. /abc$/ ist nur dann wahr, wenn der Text mit abc endet.
- \b steht für eine Wortgrenze. abc ist wahr, wenn abc als eigenes Wort im Text auftaucht ("abc def ...") und falsch, wenn abc nur Teil eines Wortes ist ( "abcd ef..." )
- \B ist das Gegenteil von \b. abc ist falsch, wenn abc als eigenes Wort im Text auftaucht ("abc def ...") und wahr, wenn abc nur Teil eines Wortes ist ( "abcd ef..." )
- | dient als oder-Verknüpfung. /abc|xyz/ ist sowohl bei abc als auch bei xyz wahr.
2. Wildcards, Suchbereiche: - . ( der Punkt ) steht für ein beliebiges Zeichen: /ab.c/ ist wahr, wenn im Text zB abxc, ab1c oder ab c steht. Es ist nicht wahr, wenn zwischen ab und c mehr als ein Zeichen steht.
- [...] Eckige Klammern definieren einen Zeichenbereich, der für ein Zeichen im Originaltext steht. HÄH? OK, ein Beispiel: Wenn zB nach einem Herrn Maier gesucht wird, aber nicht sicher ist, ob er im Text nun Maier oder Meier geschrieben wurde, kann man ihn mittels /M[ae]ier/ suchen: Die regexp gibt sowohl beim "Meier" als auch beim "Maier" wahr zurück.
Es können auch ganze Zeichen-Bereiche abgedeckt werden:
- [A-Z] trifft auf alle Großbuchstaben zu
- [a-z] trifft auf alle Kleinbuchstaben zu
- [0-9] trifft auf alle Ziffern zu
- ^ Jetzt wird's etwas irritierend, weil - den hatten wir doch gerade schon. Innerhalb der eckigen Klammern kommt dem ^ nämlich die Funktion des Negierens zu - die regexp gibt wahr zurück, wenn der Zeichenbereich nicht zutrifft. Auch hier ein Beispiel: /1234[^5]/ findet alle Zeichenfolgen 1234, denen keine 5 folgt.
3. Wiederholungsangaben:
Wiederholungsangaben machen die Suchbereiche und Wildcards noch flexibler. Sie werden direkt hinter der betroffenen Klammer bzw dem Punkt/Stern angegeben. - * steht für beliebig oft oder gar nicht. Beispiel: /abc[x]*d/ ist wahr, wenn zwischen abc und d gar nichts oder ein oder mehrere x stehen - es ist falsch, wenn irgendein anderes Zeichen dazwischen steht.
- + hat im Prinzip die gleiche Wirkung wie das *, nur das in diesem Fall die Suchzeichen mindestens einmal auftreten müssen. Beispiel: /.+/ ist wahr, wenn mindestens ein beliebiges Zeichen vorhanden ist.
- ? bedeutet: Kann da sein, muss aber nicht. Im Prinzip also wieder wie das *, nur das hier die maximale Trefferlänge 1 beträgt.
- {n1,n2} schränkt den Grössenspielraum ein: n1 steht für die Mindestanzahl Treffer, n2 für die Maximalanzahl. Die Maximalanzahl kann bB weggelassen werden. Mit Komma ( "{n1,}" ) wird die regexp wahr, wenn die Fundstelle mindestens n1 Zeichen lang ist. Ohne Komma ( "{n1}" ) wird sie wahr, wenn genau n1 Zeichen gefunden werden.
4. Maskieren
Es kann natürlich vorkommen, daß mit der regexp Zeichen gesucht werden, die innerhalb der regexp eine eigene Funktion haben ( Beispiel: . - der Punkt. ). Diesen Zeichen wird durch einen vorgestellten Backslash ( \. ) die Metazeichen-Funktion genommen und in ein ganz normales Literal umgewandelt.
Soweit alles klar?
|
|
|
24.05.2003, 21:49
|
#2
|
|
TP-Specialist
Registriert seit: Aug 2002
Ort: Nähe Wien
|
100 % von mir - da hab ich endlich mal gewisse basics kapiert :-) toll!!
|
|
|
25.05.2003, 23:59
|
#3
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Grau ist alle Theorie....
...deswegen jetzt mal ein wenig Praxis...
Nehmen wir mal an, wir hätten ein riesengrosses Logfile und wollen aus diesem alle IP-Adressen rausfiltern.
IP-Adressen sind nach einem recht einfachen Schema aufgebaut: Vier 1-3stellige Zahlen, die jeweils durch einen Punkt getrennt sind. ( An die Netzwerkgurus: Ich weiß, daß es etwas komplizierter ist, aber für's erste Beispiel reicht diese Definition...  )
Hmm...
Ziffern können durch [0-9] ermittelt werden. Die Anzahl kann durch die geschweiften Klammern eingeschränkt werden. Also wird eine der Zahlengruppen durch [0-9]{1,3} definiert. Da der Punkt eine regexp-Sonderfunktion hat, müssen wir ihn mit dem Backslash maskieren. Das war's eigentlich schon, um die regexp festzulegen:
/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/
Eigentlich simpel, oder?
Nun gut, jetzt wissen wir, daß eine Zeile eine IP_Adresse enthält, wissen aber noch nicht, welche IP-Adresse das ist. Dafür dienen die runden Klammern (...). Alle Treffer - seien es nun Literale oder (umgesetzte) Metazeichen - die innerhalb dieser runden Klammern stehen, werden an das aufrufende Script übergeben ( $1,$2... bei Perl, bei PHP im angegebenen Zielarray ). Also würde uns die regexp
/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/
die IP-Adresse in der jeweiligen Variablen zurückliefern.
...to be continued...
Geändert von webmichl (26.05.2003 um 08:14 Uhr).
|
|
|
26.05.2003, 09:35
|
#4
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Kleines Schmankerl zwischendurch...
Da unser Suchschema ja aus 4 sich wiederholenden, fast identischen Masken besteht, kann man die Sache noch verkürzen:
/(([0-9]{1,3}\.?){4})/
erfüllt den gleichen Zweck wie der Bandwurm im letzten Beitrag. Und der Einsatz des Fragezeichens dürfte auch klar sein, oder?
EDIT
Erfüllt solange den Zweck, wie korrekte IP-Adressen vorliegen. Siehe unten...
Geändert von webmichl (26.05.2003 um 21:44 Uhr).
|
|
|
26.05.2003, 10:49
|
#5
|
|
TP-Veteran
Registriert seit: Mar 2001
Ort: wein4tel (ö)
|
guter workshop, michl. freu mich schon auf weitere lektionen und beispiele 
|
|
|
26.05.2003, 13:12
|
#6
|
|
TP-Veteran
Registriert seit: Oct 2001
Ort: Oberau
|
Klasse, bisher alles verstanden,
gogo for input
|
|
|
26.05.2003, 13:18
|
#7
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Korrektur #1
Stuck Mojo hat mich gerade darauf hingewiesen, daß meine Kurzversion
/(([0-9]{1,3}\.?){4})/
etwas zu effizient ist: Eingaben wie zB 192.168........1....2.. werden auch als zutreffend behandelt. Ist mir im Moment noch rätselhaft, warum, aber hier die ( hoffentlich  ) korrekte Variante:
/(([0-9]{1,3}\.){3}([0-9]{1,3}))/
|
|
|
26.05.2003, 13:31
|
#8
|
|
TP-Veteran
Registriert seit: Oct 2001
Ort: Oberau
|
hmm hab das mal ausprobiert:
PHP-Code:
// Ausgangsstring
$string = "123.123.123.123";
// Pattern
$pattern = "/(([0-9]{1,3}\.{3})([0-9]{1,3}))/";
echo preg_match($pattern,$string,$match);
gibt mir 0 aus, wieso? Müsste doch 1 aussprucken
edit: Die Maskierung wird vom Forum geschluckt ...
|
|
|
26.05.2003, 13:39
|
#9
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Hmm... in Perl funktionierts und hier hab ich kein PHP.
Hat php bzw preg_match evtl Probleme mit dieser Verschachtelung? Funktioniert die "lange" Variante?
|
|
|
26.05.2003, 13:41
|
#10
|
|
TP-Veteran
Registriert seit: Oct 2001
Ort: Oberau
|
mit der langen fruchtets, komisch
Naja ...
|
|
|
26.05.2003, 13:58
|
#11
|
|
TP-Moderator
Registriert seit: Feb 2001
Ort: Helmstedt/Wolfsburg
|
Hi !!
Also bei mir funzt der auch:
Code:
<?
$value = "123.123.123.123";
echo preg_match("/(([0-9]{1,3}\.?){4})/",$value); // zu gefrässig...
echo preg_match("/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/",$value); // funzt
echo preg_match("/^(([0-9]{1,3}\.){3,3}[0-9]{1,3})/",$value); // funzt (aus phpMyAdmin)
echo preg_match("/(([0-9]{1,3}\.){3}([0-9]{1,3}))/",$value); // funzt
?>
@holunda: 
|
|
|
26.05.2003, 14:11
|
#12
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Ich habs!
Falsch:
$pattern = "/(([0-9]{1,3}\.{3})([0-9]{1,3}))/";
Richtig:
$pattern = "/(([0-9]{1,3}\.){3}([0-9]{1,3}))/";
|
|
|
26.05.2003, 14:12
|
#13
|
|
TP-Moderator
Registriert seit: Feb 2001
Ort: Helmstedt/Wolfsburg
|
hehe... Achsoo... Der Alex hat selber rumgespielt... Ich dachte, das war copy'n'paste 
|
|
|
26.05.2003, 18:10
|
#14
|
|
TP-Veteran
Registriert seit: Oct 2001
Ort: Oberau
|
@jan: Ruhe auf den billigen Plätzen 
sorry wollt da selber mal drehen, war woll nix
|
|
|
26.05.2003, 18:33
|
#15
|
|
TP-Special Mod
Registriert seit: Jun 2001
Ort: 8°21' O 49°1' N
|
Und weiter gehts....
Wie oben ja schon mal kurz angesprochen wurde, können auch mehrere "Fundstellen" im Text ausgewertet werden. Um dies etwas zu vertiefen, nehmen wir mal ein anderes Beispiel.
Angenommen, in einem Text tauchen Datumangaben auf, allerdings in amerikanischer Schreibweise:
2003-05-26
Diese Daten wollen wir ausfiltern und dabei gleichzeitig in Tag, Monat und Jahr aufsplitten.
Nun gut - die gleiche Vorgehensweise wie bei der IP-Adresse:
Jahr: 4stellige Zahl, also [0-9]{4}
Monat und Tag: 2stellige Zahl, also 0-9]{2}
Das Ganze noch getrennt durch Bindestriche und die regexp sieht folgendermaßen aus:
/[0-9]{4}-[0-9]{2}-[0-9]{2}/
Da wir ja die Einzelwerte ( Tag, Monat, Jahr ) haben wollen, müssen wir sie entsprechend klammern:
/([0-9]{4})-([0-9]{2})-([0-9]{2})/
In Perl stünde das Jahr jetzt in $1, der Monat in $2 und der Tag in $3.
Bei php, mit dem Aufruf
preg_match ("/([0-9]{4})-([0-9]{2})-([0-9]{2})/", $Zeile , $array)
stünde das Jahr in $array[1], der Monat in $array[2] und der Tag in $array[3].
Jetzt wäre es natürlich schön, wenn man das Datum direkt von der amerikanischen in die deutsche Schreibweise umwandeln könnten - ohne den Umweg über irgendwelche Scriptlösungen.
Wie das wohl zu lösen ist? 
|
|
|
|
Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)
|
|
|
| Themen-Optionen |
Thema durchsuchen |
|
|
|
Forumregeln
|
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge anzufügen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
HTML-Code ist Aus.
|
|
|
Alle Zeitangaben in WEZ +2. Es ist jetzt 03:16 Uhr.
|
 |