Mazze: regexp für verschachtelte links

Hi zusammen,

eine knifflige Frage, zu der ich leider noch nichts finden konnte:

Würde gerne aus einem xml-snippet dieser Art

<bar>abc <foo>innen anfang <foo>ganz innen </foo>blindtext </foo>innen ende <foo>auch innen </foo>xyu </bar>

die einzelnen <foo>-Bereiche z.B. via regexp extrahieren (um sie weiter zu verarbeiten, und durch was anderes zu ersetzen)

Das Problem ist hierbei die Verschachtelung, denn ein regexp der Art

$code = preg_replace('/<foo>(.+?)</foo>/is', $replaces , $code);

würde ja alles vom ersten <foo> bis zum ersten </foo> ersetzen und damit die Verschachtelung ignorieren.

Hat jemand eine Idee, ob das mit regexp überhaupt geht, bzw. wie man es sonst machen könnte?

Vielen Dank für die Mithilfe,

Mazze

  1. Hallo Mazze,

    würde ja alles vom ersten <foo> bis zum ersten </foo> ersetzen und damit die Verschachtelung ignorieren.
    Hat jemand eine Idee, ob das mit regexp überhaupt geht, bzw. wie man es sonst machen könnte?

    Ja. Such mal nach "ungreedy".

    Grüße aus Freiburg,
    Marian

    --
    Microsoft broke Volkswagen's world record: Volkswagen made only 22 million bugs!
    <!--[if IE]><meta http-equiv="refresh" content="0; URL=http://www.getfirefox.com"><[endif]-->
    1. Ja. Such mal nach "ungreedy".

      ungreedy wäre in dem Fall mit der Option U noch hinten dran, dass wird aber so trotzdem nicht funktionieren, da er dann immer vom ersten Vorkommen von <foo> bis zum ersten Vorkommen von </foo> alles erkennen würde. Im obrigem Beispiel also:

      <foo>innen anfang <foo>ganz innen </foo>
      <foo>ganz innen </foo>
      <foo>auch innen </foo>

      du müsstest also im regulären Ausdruck deutlich machen, dass ff. Situation gültig wäre:

      <foo></foo>
      ODER
      <foo><foo></foo></foo>
      Aber niemals
      <foo><foo></foo>
      womit man dann aber vor nem schönen Problem steht

      also ich würds eher ohne regulären Ausdruck versuchen zu teilen ;)

      1. Hi zusammen,

        Ja. Such mal nach "ungreedy".

        wie skafoi schon beschrieben hat, ist das Problem damit nicht zu lösen.

        womit man dann aber vor nem schönen Problem steht

        Deswegen hab ich ja hier ins Forum den Eintrag geschrieben ;-)

        Leider erlaubt simple_xml kein ersetzen von tags im xml-file. Und mit DOM hab ichs noch nicht rausgefunden, wäre aber auch arg mit Spatzen auf Kanonen geschissen.

        Danke trotzdem schon mal für die Mühen,

        Mazze

  2. Hat jemand eine Idee, ob das mit regexp überhaupt geht, bzw. wie man es sonst machen könnte?

    Hier noch ein kleines Beispiel wie so etwas ohne regulären Ausdruck funktionieren könnte. Ist nur schnell hinprogrammiert - übernehme somit keine Haftung für Fehler. Dauert zudem mMn recht lange, das optimieren überlasse ich da aber gerne dir. Die Funktion remove_parent_attribute hab ich nur eingebaut, damit man anschließend auch das ganze schön mit print_r ausgeben lassen kann und nicht versucht das durch ['parent'] entstandene rekursive Array auszugeben.

    <?php

    $text = '<bar>abc <foo>innen anfang <foo>ganz innen </foo>blindtext </foo>innen ende <foo>auch innen </foo>xyu </bar>';

    $data = array();
    $data['parent'] = null;
    $data['type'] = 'base';
    $data['children'] = array();
    $data['length'] = 0;

    $data_pointer = &$data;

    $open_token = array('foo', 'bar');
    $close_token = array('/foo', '/bar');

    $temp_expl = explode('<', $text);

    foreach($temp_expl as $temp_value) {

    if (empty($temp_value)) continue;

    $temp_expl2 = explode('>', $temp_value);

    if (in_array($temp_expl2[0], $open_token)) {

    $temp_a = array();
      $temp_a['parent'] = &$data_pointer;
      $temp_a['type'] = $temp_expl2[0];
      $temp_a['children'] = array();
      $temp_a['length'] = 0;

    $data_pointer['children'][++$data_pointer['length']] = $temp_a;

    $data_pointer = &$data_pointer['children'][$data_pointer['length']];

    } else if (in_array($temp_expl2[0], $close_token)) {

    $temp_type = substr($temp_expl2[0], 1);
      if ($temp_type == $data_pointer['type']) {
       $data_pointer = &$data_pointer['parent'];
      } else {
       // fehler beim parsen
      }

    } else {

    // fehler beim parsen

    }

    if (!empty($temp_expl2[1])) {

    $temp_a = array();
      $temp_a['type'] = 'text';
      $temp_a['length'] = strlen($temp_expl2[1]);
      $temp_a['text_value'] = $temp_expl2[1];

    $data_pointer['children'][++$data_pointer['length']] = $temp_a;

    }

    if (!empty($temp_expl2[2])) {

    // fehler beim parsen

    }

    }

    function remove_parent_attribute(&$data) {
     if (!empty($data['children'])) foreach($data['children'] as $k=>$v) {
      remove_parent_attribute($data['children'][$k]);
     }
     unset($data['parent']);
    }

    remove_parent_attribute($data);

    print_r($data);

    ?>

    1. Hi Skafoi,

      vielen Dank für den ausführlichen Code! Scheint auch zu funktionieren. Aber - ich hab nach einem halben Tag Google-Recherche dann doch was sehr interessantes gefunden, was mein Problem tatsächlich mit einem regexp löst:

      http://blog.stevenlevithan.com/regular-expressions/match-innermost-html-element/