Felix Riesterer: (PHP) verstehe meine regexp nimmer...

Liebes Forum,

ich möchte auf meiner Seite zwecks einfacher Wartung Unterseiten (die meistens von anderen Autoren stammen) mittles PHP im body-Bereich auslesen und in meiner index.php anzeigen. Dazu ist es oft nötig, Verweisziele zu korrigieren, da der relative Pfad der originalen HTML-Dateien ein anderer ist, als meine index.php.

Mit den Bilderquellen tut das wunderbar per

$html_body = preg_replace('/((?i)src=)"?([\S^>^"]*)/', '\1"'.$ref_korrektur.'\2', $html_body);

Aber bei den Links ist das etwas komplizierter. Es sollen nur relative Links modifiziert werden; Verweisziele wie "http://..." und "mailto:..." sollen unverändert bleiben. Ich scheitere daran, in meiner regexp eben diese Fälle erfolgreich auszuschließen. So weit bin ich heute nacht noch gekommen:

$html_body = preg_replace('/((?i)href=)"?(?!(mailto:[\S^>^"]*)|(http:[\S^>^"]*))([\S^>^"]*)/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

Mir sind gleich zwei Dinge nicht klar:
 1. Warum scheitert meine regexp nicht an "mailto:" und "http:"?
 2. Woher kommen denn die schließenden Anführungszeichen (sowohl bei den Bildquellen, als auch bei den Verweiszielen)?

Bin um jeden Denkanstoß dankbar.

Liebe Grüße aus Ellwangen,

Felix Riesterer.

  1. Hallo Felix!

    $html_body = preg_replace('/((?i)href=)"?(?!(mailto:[\S^>^"]*)|(http:[\S^>^"]*))([\S^>^"]*)/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

    Tut mir Leid, aber das sieht mir für diese Uhrzeit und überhaupt irgendwie zu wirr aus ;)

    Vielleicht hast Du irgendwo noch Klammern vergessen?
    Ich kenn mich mit RegEx nicht so wirklich gut aus, aber "!(mailto:[\S^>^"]*) | (http:[\S^>^"]*)" würde ich sagen negiert höchstens das mailto-Zeugs, aber auf jeden Fall nicht die http-Adressen (oder bindet das ! über den | auch ohne Klammern?).

    So, mehr fällt mir dazu aber nicht mehr ein, ich geh erstmal schlafen.

    MfG
    Götz

    --
    Losung für Donnerstag, 2. Dezember 2004
    Als ich den Herrn suchte, antwortete er mir und errettete mich aus aller meiner Furcht. (Psalm 34,5)
    Maria singt: Des Herrn Barmherzigkeit währt von Geschlecht zu Geschlecht bei denen, die ihn fürchten. (Lukas 1,50)
    (Losungslink)
  2. gudn tach!

    $html_body = preg_replace('/((?i)href=)"?(?!(mailto:[\S^>^"]*)|(http:[\S^>^"]*))([\S^>^"]*)/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

    1. Warum scheitert meine regexp nicht an "mailto:" und "http:"?

    wegen dem fragezeichen im "? davor, denke ich.

    1. Woher kommen denn die schließenden Anführungszeichen (sowohl bei den Bildquellen, als auch bei den Verweiszielen)?

    aeh, du moechtest wissen, wie du du sie einfuegen kannst, oder?

    Bin um jeden Denkanstoß dankbar.

    zuerst s/href=([^"])/href="\1/g und dann
    $html_body=preg_replace('/((?i)href=)"(?!(?:mailto:|http:))([\S^>^"]*)(?:")?/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2"', $html_body);

    prost
    seth

    1. Hi seth,

      $html_body = preg_replace('/((?i)href=)"?(?!(mailto:[\S^>^"]*)|(http:[\S^>^"]*))([\S^>^"]*)/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

      1. Warum scheitert meine regexp nicht an "mailto:" und "http:"?

      wegen dem fragezeichen im "? davor, denke ich.

      Nein, das Fragezeichen sagt nur, dass das " optional ist, also auch fehlen kann.

      1. Woher kommen denn die schließenden Anführungszeichen (sowohl bei den Bildquellen, als auch bei den Verweiszielen)?

      aeh, du moechtest wissen, wie du du sie einfuegen kannst, oder?

      Nein, im Ergebnisstring sind nach dem src-Attribut schließende Anführungszeichen enthalten, die in meinem Ersetzungsargument nicht enthalten sind, also auch nicht eingefügt werden sollen...
      Aber ich ahne schon den Fehler: Ich habe die eventuellen schließenden Anführungszeichen nicht im Suchstring... also bleiben sie "übrig". Werde das testen.

      zuerst s/href=([^"])/href="\1/g und dann

      Verstehe ich (noch) nicht...

      $html_body=preg_replace('/((?i)href=)"(?!(?:mailto:|http:))([\S^>^"]*)(?:")?/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2"', $html_body);

      Das werde ich mal ganz schnell ausprobieren. Melde dann, wenn es tut.

      prost

      Jedenfalls Danke für Deine Antwort!

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

      1. Hi seth,

        Das mit

        $html_body=preg_replace('/((?i)href=)"(?!(?:mailto:|http:))([\S^>^"]*)(?:")?/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2"', $html_body);

        klappt schon ganz gut. Allerdings wird ein Link (href="datei.html") nicht mit "index.php?aktion= usw. " korrigiert, sondern nur mit dem Inhalt von $ref_korrektur. Bei anderen Links habe ich doppelte schließende Anführungszeichen ("... /datei.html""). Das mit den Anführungszeichen hatte ich ja schon bei den src-Referenzierungen nicht verstanden...

        Ich bleibe dran. Muss nur mal eben weiter mein Geld verdienen gehen. ;-)

        Liebe Grüße aus Ellwangen,

        Felix Riesterer.

      2. gudn tach!

        1. Warum scheitert meine regexp nicht an "mailto:" und "http:"?

        wegen dem fragezeichen im "? davor, denke ich.
        Nein, das Fragezeichen sagt nur, dass das " optional ist, also auch fehlen kann.

        ja, das weiss ich. ich dachte jedoch, dass eben gerade diese optionalitaet in konflikt geraten koennte mit dem zero-width negative look-ahead-muster (?!...). vielleicht kann da jemand etwas zu sagen, der's genau weiss.

        das eigentliche problem lag an etwas ganz anderem:
        [\S^>^"] macht nicht, was du moechtest. hab mir das heute mittag gar nicht genau genug angeschaut. das negations-dach ^ kann nur eine komplette zeichenklasse negieren, also z.b. [^a-zA-Z] sind alle zeichen, ausser buchstaben. [\S^>^"] ist das gleiche wie [\S^>"] (^ als zeichen, nicht als negation). und das ist das gleiche wie \S, (ohne komma) da ^>" bestandteil von \S sind.

        zuerst s/href=([^"])/href="\1/g und dann
        Verstehe ich (noch) nicht...

        sorry, ich dachte, dass jeder, der sich mit regulaeren ausdruecken beschaeftigt hat, auch die perl-syntax kennt.
        ich meinte, dass ggf. " ergaenzt werden soll...

        $html_body=preg_replace('/((?i)href=)"(?!(?:mailto:|http:))([\S^>^"]*)(?:")?/', '\1"index.php?aktion=informationen&thema='.$ref_korrektur.'\2"', $html_body);

        hinten der teil (?:")? ist uebrigens doof geschrieben (ist mir aber erst eingefallen, als ich schon nicht mehr am rechner sass). besser ist einfach "?, also waere der vollstaendige kram nun:

        $html_body=preg_replace('/href=([^"> ]+)([> ])/', 'href="\1"\2', $html_body);
        $html_body=preg_replace('/((?i)src=)"?([^>" ]*)"?/', '\1"'.$ref_korrektur.'\2"', $html_body);
        $html_body=preg_replace('/((?i)href=")(?!(?:mailto:|http:))([^>" ]*")/', '\1index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

        prost
        seth

        1. Du bischt mein Held!

          Verstehe ich (noch) nicht...
          sorry, ich dachte, dass jeder, der sich mit regulaeren ausdruecken beschaeftigt hat, auch die perl-syntax kennt.

          Ich habe mich da gestern abend zum ersten Mal durch die Seite http://www.regularexpressions.info/tutorial.html gekämpft, um die Funktionsweise dieser regulären Ausfdrücke generell zu verstehen. Die Zeichenfolge s/ verstehe ich (noch) nicht, da ich noch lernen muss, was der Slash inmitten einer regexp bedeutet. Auch das mit dem Doppelpunkt und der Backreference, ebenso Lookahead/-behind machen mir noch Kopfzerbrechen.

          Deine Codezeilen funktionieren einfach göttlich!
          Ich muss jetzt nur noch herausfinden für welche Dateitypen ich die relativen Verweisziele auch nicht korrigiert haben will... it never ends, does it? ;-)

          Vielen Dank für die Komplettlösung! Das mit dem programmierten Unsinn in [\S^>^"] habe ich begriffen.

          Liebe Grüße aus Ellwangen,

          Felix Riesterer.

          1. gudn tach!

            sorry, ich dachte, dass jeder, der sich mit regulaeren ausdruecken beschaeftigt hat, auch die perl-syntax kennt.
            Ich habe mich da gestern abend zum ersten Mal durch die Seite http://www.regularexpressions.info/tutorial.html gekämpft, um die Funktionsweise dieser regulären Ausfdrücke generell zu verstehen.

            wow, dafuer, war dein ansatz aber schon sehr gut.

            Die Zeichenfolge s/ verstehe ich (noch) nicht, da ich noch lernen muss, was der Slash inmitten einer regexp bedeutet.

            in mitten eines regulaeren ausdrucks hat das keine spezielle bedeutung. s/ leitet in perl ein suchen-und-ersetzen-regexp ein. http://de.selfhtml.org/cgiperl/sprache/regexpr.htm#suchen_ersetzen

            Auch das mit dem Doppelpunkt und der Backreference, ebenso Lookahead/-behind machen mir noch Kopfzerbrechen.

            www.perldoc.com ist irgendwie momentan nicht erreichbar. naja, auf http://search.cpan.org/~nwclark/perl-5.8.6/pod/perlre.pod#Extended_Patterns steht's auch.

            Ich muss jetzt nur noch herausfinden für welche Dateitypen ich die relativen Verweisziele auch nicht korrigiert haben will...

            dafuer musst du bloss den teil ([^>" ]*") abaendern, z.b. werden vom folgenden muster nur verweise zugelassen, die auf htm(l) oder php enden:

            ([^>" ]*(?:html|htm|php)")

            ?: bedeutet, dass der kram in der klammer nicht zwischengespeichert wird, um darauf zurueckzureferenzieren.

            prost
            seth

            1. Hi seth,

              ich fühle mich gelobt, und daher sehr geschmeichelt. :-))

              Ich muss jetzt nur noch herausfinden für welche Dateitypen ich die relativen Verweisziele auch nicht korrigiert haben will...

              dafuer musst du bloss den teil ([^>" ]*") abaendern, z.b. werden vom folgenden muster nur verweise zugelassen, die auf htm(l) oder php enden:

              ([^>" ]*(?:html|htm|php)")

              Das ist _wirklich_ _große_ _klasse_!!

              Damit kann ich zuverlässig alle meine Verweise auf HTML-Dokumente "korrigieren", so dass deren Body-Bereich in meiner index.php geparst (und gesäubert!) angezeigt werden kann.

              Nun habe ich versucht, die Verweise auf nicht-HTML-Dateien (also alles, was nicht htm(l) als Dateiendung hat) nur in der Pfadangabe zu korrigieren, da ja diese Links weiterhin wie in der originalen HTML-Datei funktionieren sollen.

              Ich habe diese ganze Programmzeile hinzugefügt:

              $html_body=preg_replace('/((?i)href=")(?!(?:mailto:|http:))([^>" ]*(?!(?:html|htm))")/', '\1'.$ref_korrektur.'\2', $html_body);

              Allerdings lässt dieser Ausdruck die Finger nicht von htm(l)-Dateien... Ich dachte, ich modifiziere den von Dir vorgeschlagenen Bereich

              ([^>" ]*(?:html|htm|php)")

              in meiner neuen Programmzeile nach
              ([^>" ]*(?!(?:html|htm))")
              was ja syntaktisch genauso funktioniert, wie der Ausschluss von mailto: und http: -Adressen (ist das nicht negative lookahead?)

              Irgendwas klemmt da aber noch, da durch diese Programmzeile bereits "korrigierte" Links nocheinmal modifiziert werden...

              Hier mein Ausschnitt aus dem Programm:
              // Referenzierungen korrigieren

              // Bildquellen "reparieren"

              $html_body=preg_replace('/((?i)src=)"?([^>" ]*)"?/', '\1"'.$ref_korrektur.'\2"', $html_body);

              // Verweisziele "reparieren"

              // sorge dafür, dass Verweisziele in Anführungszeichen stehen

              $html_body=preg_replace('/href=([^"> ]+)([> ])/', 'href="\1"\2', $html_body);

              // korrigiere Verweise auf alle Dateitypen deren Endung nicht htm(l) ist, ohne sie über die index.php laufen zu lassen

              $html_body=preg_replace('/((?i)href=")(?!(?:mailto:|http:))([^>" ]*(?!(?:html|htm))")/', '\1'.$ref_korrektur.'\2', $html_body);

              // verbiege Verweisziele auf HTML-Dateien mit der Endung htm(l), damit sie über die index.php ausgelesen werden

              $html_body=preg_replace('/((?i)href=")(?!(?:mailto:|http:))([^>" ]*(?:html|htm)")/', '\1index.php?aktion=informationen&thema='.$ref_korrektur.'\2', $html_body);

              Hmm... ich werde morgen daran weiterprobieren.

              Jedenfalls Dir ganz großen Dank, denn Du hast mir viel Stoff zum Durchdenken und Nachverstehen gegeben.

              Liebe Grüße aus Ellwangen,

              Felix Riesterer.

              1. gudn tach Felix!

                Ich dachte, ich modifiziere den von Dir vorgeschlagenen Bereich

                ([^>" ]*(?:html|htm|php)")
                in meiner neuen Programmzeile nach
                ([^>" ]*(?!(?:html|htm))")
                was ja syntaktisch genauso funktioniert, wie der Ausschluss von mailto: und http: -Adressen (ist das nicht negative lookahead?)

                hier benoetigst du einen zero-width negative look-behind. ;-)
                (?<!html|htm)"

                denn, _vor_ dem " soll weder html noch htm stehen.

                ([^>" ]*(?!html|htm)") wuerde versuchen ein muster zu finden, bei dem nach [^>" ]* kein html? folgt, z.b. bei
                datei.htm"
                folgt nach
                datei.htm
                kein html?, (ohne komma) sondern ein ". (ohne punkt) deswegen wird alles gematcht.

                prost
                seth

                1. Hi seth,

                  hier benoetigst du einen zero-width negative look-behind. ;-)
                  (?<!html|htm)"
                  denn, _vor_ dem " soll weder html noch htm stehen.

                  DAS war's! Da hatte ich wieder mal einen Denkfehler.
                  Diese regulären Ausdrücke sind wirklich ein mächtiges Werkzeug... wenn man es zu gebrauchen versteht. ;-) Diese Funktionalitäten hatte ich nämlich zunächst mit sehr umständlichen for-Schleifen "nachprogrammiert", was lokal auf meinem Apache auch tat, bei Funpic sogar auch, auf dem beabsichtigten Webspace aber plötzlich nimmer (maxtime exeeded (>30sec)). Und mit preg_replace und genialem Suchmuster tut das nicht nur, sondern ist auch ne riesige Menge weniger (und edlerer) Quellcode.

                  Ich danke Dir von Herzen! Man liest sich wieder.

                  prost
                  seth

                  Mahlzeit, und frohe Spekulatius!
                  Liebe Grüße aus Ellwangen,

                  Felix Riesterer.

                  1. gudn tach Felix!

                    Diese regulären Ausdrücke sind wirklich ein mächtiges Werkzeug... wenn man es zu gebrauchen versteht. ;-) Diese Funktionalitäten hatte ich nämlich zunächst mit sehr umständlichen for-Schleifen "nachprogrammiert" [...]. Und mit preg_replace und genialem Suchmuster tut das nicht nur, sondern ist auch ne riesige Menge weniger (und edlerer) Quellcode.

                    oh, ja, regexp sind schweinemaechtig, schoen kurz, bequem und sehen klasse aus. hmm, ok, letzteres ist wohl geschmackssache. ;-)
                    aaaber in sehr vielen faellen ist ein algorithmus schneller, der auf die verwendung von regexp verzichtet. aus diesem grunde, sollte man, sobald die laufzeit eine rolle spielt, wenn moeglich, auch immer erst mal versuchen ohne regexp auszukommen.

                    von dem von dir genannten problem allerdings denke ich, dass man dort (wenn ueberhaupt) keinen grossen geschwindigkeitszuwachs herausholen koennte.

                    prost
                    seth

        2. gudn tach!

          1. Warum scheitert meine regexp nicht an "mailto:" und "http:"?

          wegen dem fragezeichen im "? davor, denke ich.
          Nein, das Fragezeichen sagt nur, dass das " optional ist, also auch fehlen kann.

          ja, das weiss ich. ich dachte jedoch, dass eben gerade diese optionalitaet in konflikt geraten koennte mit dem zero-width negative look-ahead-muster (?!...). vielleicht kann da jemand etwas zu sagen, der's genau weiss.

          hab mir noch mal gedanken darueber gemacht:
          angenommen wir durch suchen den string
          $html_body='<a href="http://www.bierdatenbank.de/index.php">bdb</a>';

          dann findet
          /((?i)href=)"?(?!mailto:|http:)([^>" ]*)"?/
          folgendes:

          regexp             |  gefunden
          -------------------+-----------------------------------------
          ((?i)href=)        |  href=
          "?                 |  (kein ")
          (?!mailto:|http:)  |  (also auch kein mailto: oder http:)
          ([^>" ]*)          |  (nix, da " naechstes zeichen)
          "?                 |  " (weil gierig)

          also insg. wir bloss href=" gefunden.

          deswegen besser vorher, wie im anderen ast bereits erlaeutert, die urls mit " vorne und hinten versehen und dann:
          /((?i)href=")(?!mailto:|http:)([^>" ]*")/

          prost
          seth