Seite 1 von 9 1234 ... LetzteLetzte
Ergebnis 1 bis 15 von 121

Thema: [Workshop] Regular Expressions

  1. #1
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    [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?
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  2. #2
    TP-Specialist
    Registriert seit
    Aug 2002
    Ort
    Nähe Wien
    Beiträge
    4.643
    100 % von mir - da hab ich endlich mal gewisse basics kapiert :-) toll!!

  3. #3
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    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 09:14 Uhr)
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  4. #4
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    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 22:44 Uhr)
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  5. #5
    TP-Veteran Avatar von steff
    Registriert seit
    Mar 2001
    Ort
    wein4tel (ö)
    Beiträge
    1.291
    guter workshop, michl. freu mich schon auf weitere lektionen und beispiele

  6. #6
    TP-Veteran Avatar von holunda
    Registriert seit
    Oct 2001
    Ort
    Oberau
    Beiträge
    1.867
    Klasse, bisher alles verstanden,
    gogo for input

  7. #7
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    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}))/
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  8. #8
    TP-Veteran Avatar von holunda
    Registriert seit
    Oct 2001
    Ort
    Oberau
    Beiträge
    1.867
    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 ...

  9. #9
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695
    Hmm... in Perl funktionierts und hier hab ich kein PHP.

    Hat php bzw preg_match evtl Probleme mit dieser Verschachtelung? Funktioniert die "lange" Variante?
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  10. #10
    TP-Veteran Avatar von holunda
    Registriert seit
    Oct 2001
    Ort
    Oberau
    Beiträge
    1.867
    mit der langen fruchtets, komisch
    Naja ...

  11. #11
    TP-Specialist Avatar von Stuck Mojo
    Registriert seit
    Feb 2001
    Ort
    Helmstedt/Wolfsburg
    Beiträge
    4.108
    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:

  12. #12
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    Ich habs!

    Falsch:

    $pattern = "/(([0-9]{1,3}\.{3})([0-9]{1,3}))/";

    Richtig:

    $pattern = "/(([0-9]{1,3}\.){3}([0-9]{1,3}))/";
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

  13. #13
    TP-Specialist Avatar von Stuck Mojo
    Registriert seit
    Feb 2001
    Ort
    Helmstedt/Wolfsburg
    Beiträge
    4.108
    hehe... Achsoo... Der Alex hat selber rumgespielt... Ich dachte, das war copy'n'paste

  14. #14
    TP-Veteran Avatar von holunda
    Registriert seit
    Oct 2001
    Ort
    Oberau
    Beiträge
    1.867
    @jan: Ruhe auf den billigen Plätzen
    sorry wollt da selber mal drehen, war woll nix

  15. #15
    TP-Special Mod Avatar von webmichl
    Registriert seit
    Jun 2001
    Ort
    8°21' O 49°1' N
    Beiträge
    8.695

    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?
    Gruß, der Michl


    * * * if you want them to RTFM, make a better FM! * * *

Seite 1 von 9 1234 ... LetzteLetzte

Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 3 (Registrierte Benutzer: 0, Gäste: 3)


Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 3 (Registrierte Benutzer: 0, Gäste: 3)

     

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51