Jakob: Reguläre Ausdrücke: Nur außerhalb von HTML-Tags ersetzen

Hallo,

Ich habe ein Problem mit den regulären Ausdrücken.
Und zwar will ich innerhalb einer (dynamisch generierten) HTML-Seite bestimmte Worte automatisch verlinken lassen.
Dass ist ja mit regulären Ausdrücken kein Problem, dachte ich zumindest.

Mit preg_replace klappt das alles wunderbar, allerdings nur so lange sich die zu ersetzenden Worte nicht bereits innerhalb eines Links befinden. Denn dann erhalte ich plötzlich einen verschachtelten Link, und somit funktioniert dieser nicht mehr korrekt.
Noch schlimmer ist es, wenn sich das Wort zum Beispiel in einem alt-Parameter eines Bildes befindet. Dann wird nämlich die Seite komplett verstümmelt.

Nun meine Frage: Wie kann ich preg_replace dazu bringen, dass nur Worte außerdhalb von HTML-Tags ersetzt werden. Um zu veranschaulichen, was ich genau meine hier ein Beispiel:

Es soll das Wort "PHP" verlinkt werden.

Zunächst 3 Teilstrings:
1. <img src="img.jpg" alt="PHP ist toll" />
2. <a href="http://www.php.net">Zur PHP Seite</a>
3. Dies ist ein Test mit PHP usw.

Hier soll in den ersten beiden fällen das Wort PHP nicht ersetzt werden, sondern nur im dritten Fall.

Ich habe mir da schon den Kopf zerbrochen, aber keinen vernünftigen Einfall, auch nicht mit preg_replace_callback.

Kann mir hier jemand einen Denkanstoß geben?

Danke schon im voraus.

Gruß
Jakob

  1. Hi,

    Es soll das Wort "PHP" verlinkt werden.

    Zunächst 3 Teilstrings:

    1. <img src="img.jpg" alt="PHP ist toll" />
    2. <a href="http://www.php.net">Zur PHP Seite</a>
    3. Dies ist ein Test mit PHP usw.

    Hier soll in den ersten beiden fällen das Wort PHP nicht ersetzt werden, sondern nur im dritten Fall.

    Vielleicht möchtest du das Vorkommen des Suchstrings, ja nur in bestimmten Tags ersetzen, zb etwa so:

    $find="welt";
    $test="ging <span>in die weite welt hinein</span>, blablu";

    $tags='span|div|p|td';
    $pattern='/(<('.$tags.')[^>]*>[^<]*)('.$find.'\b)([^<]*</\2>)/i';
    $replace='\1<a href="#">\3</a>\4';

    echo preg_replace($pattern,$replace,$test);

    herzliche Grüße,
    Jonny 5

    1. Hallo,

      erstmal danke für diesen Tipp.
      Leider funktioniert das auch nicht korrekt.
      Denn wenn zum Beispiel ein Link innerhalb eines span-Tags enthalten ist, dann werden innerhalb dieses span-Tags gar keine Ersetzungen vorgenommen.

      Also hier bei Beispiel bei dem es nicht mehr funktioniert:

      $test="ging <span>in die <a href="d.htm">welt</a> weite welt hinein</span>, blablu";

      Wenn man das noch irgendwie lösen könnte, wäre diese Lösung natürlich ideal.

      Gruß
      Jakob

  2. hi Jakob,

    mit regular expressions ist das nicht optimal zu lösen, da man mit ihnen nur schlecht verschachteltes Markup wie HTML verarbeiten kann. besser wäre es, eine art HTML-Parser selbst zu schreiben, der zuverlässig erkennen kann, ob man sich gerade innerhalb eines bestimmten Tags (in diesem Fall <a>) befindet. so etwas gibt es bestimmt schon in php, ich habe allerdings gerade keinen Link zu Hand.

    Alternativ kannst du, was wohl am einfachsten wäre, vor dem ersetzen mit preg_replace alle <a>'s aus dem string herausnehmen und danach wieder einsetzen. vorraussetzung: valides XHTML, aber das sollte ja eh der fall sein ;P

    <?php
        ####### $content ist der zu verarbeitende Text, woher auch immer der kommt
        $content = 'asdas Test dasd <span><a href="">Test</a> Test <a href="">asd Test</a></span>';

    $subChar = chr(26); // ASCII substitution character

    preg_match_all("@<a[^>]*>(.*?)</a>@", $content, $matches);
        if (!empty($matches[0])) {
            $links = $matches[0];
            $linkPatterns = array();
            for($i=0; $i<sizeof($links); $i++) {
                // why isn't quotemeta escaping the "/" characters?
                $linkPatterns[$i] = "/".preg_replace("///", "\/", quotemeta($links[$i]))."/";
            }
            $content = preg_replace($linkPatterns, $subChar, $content, 1);
        }

    ####### Hier kommen deine eigenen Ersetzungen hin
        $content = str_replace('Test', 'Test2', $content);

    if (!empty($matches[0])) {
            $content = preg_replace(array_fill(0, sizeof($links), "/".$subChar."/"), $links, $content, 1);
        }
    ?>

    Gruß,
    Niklas

    1. ups, hab das mit den anderen Tags überlesen :P

      hier nochmal richtig:

      <?php
          ####### $content ist der zu verarbeitende Text, woher auch immer der kommt
          $content = 'asdas <img title="Test" /> Test dasd <span><a href="">Test</a> Test <a href="">asd Test</a></span>';

      $subChar = chr(26); // ASCII substitution character

      preg_match_all("@(<a[^>]*>(.*?)</a>)|(<[^a][^>]*>)@", $content, $matches);
          if (!empty($matches[0])) {
              $links = $matches[0];
              $linkPatterns = array();
              for($i=0; $i<sizeof($links); $i++) {
                  // why isn't quotemeta escaping the "/" characters?
                  $linkPatterns[$i] = "/".preg_replace("///", "\/", quotemeta($links[$i]))."/";
              }
              $content = preg_replace($linkPatterns, $subChar, $content, 1);
          }

      ####### Hier kommen deine eigenen Ersetzungen hin
          $content = str_replace('Test', 'Test2', $content);

      if (!empty($matches[0])) {
              $content = preg_replace(array_fill(0, sizeof($links), "/".$subChar."/"), $links, $content, 1);
          }

      ####### Was dann wohl mit dem Content gemacht wird?
          echo $content;
      ?>

      Hoffe das hilft.

      Gruß,
      Niklas

      1. Hallo,

        Vielen Dank, das hat funktioniert.
        Nur ich sehe nicht, wo hier die img-Tags berücksichtigt werden. Ich meine es funktioniert, aber wär toll, wenn du mir noch sagen könntest, wo dies der Fall ist.

        Gruß
        Jakob

        1. Nur ich sehe nicht, wo hier die img-Tags berücksichtigt werden. Ich meine es funktioniert, aber wär toll, wenn du mir noch sagen könntest, wo dies der Fall ist.

          Der reguläre Ausdruck "@(<a[^>]*>(.*?)</a>)|(<[^a][^>]*>)@" matcht alle <a>-Tags samt Inhalt und alles ander, und außerdem (mit dem | möglich gemacht) alles, was zwischen < und > steht (außer a sonst würde ja doppelt ersetzt werden).

          Gruß,
          Niklas

  3. gudn tach!

    Ich habe ein Problem mit den regulären Ausdrücken.
    Und zwar will ich innerhalb einer (dynamisch generierten) HTML-Seite bestimmte Worte automatisch verlinken lassen.

    ich habe mir den code von codeslayer jetzt nicht angeschaut, aber vielleicht ist ja auch die alternative, die in  http://www.php-faq.de/q/q-regexp-ersetzen.html genannt wird, was fuer dich.

    prost
    seth