Heizer: RegExp: Eigenen Code in gültiges HTML umsetzen

Liebes Forum

Ich habe eine Eingabemaske für Dateiinhalte geschrieben (mini-CMS), daß folgendes Erkennt:

*(fetter Text)*                 =>  <strong>fetter Text</strong>
/(kursiver Text)/               =>  <em>kursiver Text</em>
_(unterstrichener Text)_        =>  <span class='u'>unterstrichener Text</span>

das funktioniert auch wunderbar, auch die kombination bekomme ich hin, also z.B. _/*(fett kursiv unterstrichen)_*/

Das Problem ist, wenn die Tags falsch verschachtelt werden:

*(fetter Text /(kursiver fetter Text)* kursiver Text)/

Ich würde hier gerne folgendes erhalten:
<strong>fetter Text <em>kursiver fetter Text</em></strong><em> kursiver Text</em>

So wäre es dann valide. Aber ich weiß nicht wie bzw. ob überhaupt man das mit RegExp machen kann, und wenn nicht, wie ich dann weiterdenken soll.

Brauche einen Anstoß

Danke

Heizer

  1. Hallo

    Wieso machst du es nicht so?

    $subject = "{DICK}Dick, {STRICH}unterstrichen{/STRICH}{/DICK} und so weiter ...";
    $search  = array('{DICK}', '{/DICK}', '{STRICH}', '{/STRICH}');
    $replace = array('<b>', '</b>', '<u>', '</u>');

    $result = str_replace($search, $replace, $subject);

    1. Hallo Till,

      Das loest das Problem nicht, weil zB. aus
      {DICK}Dick, {STRICH}unterstrichen{/DICK}{/STRICH} (falsche Schachtelung)
      <b>   Dick   <u>    unterstrichen </b>   </u>     wird, also wieder falsche Schachtelung.

      Gruß,

      Dieter

  2. Hallo Heizer,

    Es gibt 12 moegliche Kombination, naemlich 6 dreifache:
    _/*,  _*/,  */_ ,  *_/,  /*_ ,  /_*,

    und 6 doppelte:
    /*,  */,  /_ ,  _/,  *_,   _*,

    Du setzt eine Prioritaet in der Reihenfolge zB. _*/, dann entfallen aus den dreifachen
    _*/

    und aus den doppelten
    _/, */ und _*

    Der Rest ist falsch. Wenn Du die Falschen in eine Klammer packst mit je einem | dazwischen, das ganze fuer beiden Gruppen und mit preg_replace() durch die Richtigen ersetzt, sollte das hinhauen. Ein paar \ gehoeren natuerlich auch noch mit rein. Im Ergebnis hast Du immer _*/, _/, /* oder _* und damit kannst Du ja umgehen.

    Ich hoffe, dass ich hier vor lauter /, * und _ nichts verwechselt habe, aber das Prinzip sollte klar sein.

    Gruß,

    Dieter

    1. Sorry, aber ich steig da nicht durch

      Du setzt eine Prioritaet in der Reihenfolge zB. _*/, dann entfallen aus den dreifachen
      _*/

      und aus den doppelten
      _/, */ und _*

      was meinst du mit einer Priorität wo soll ich die womit setzen?

      Vielleicht könntest du etwas mehr echte preg-syntax schreien, ich kappiere wirklich nicht, was du genau meinst.

      Einstweilen vielen Dank

      Heizer

      1. Hallo Heizer,

        Ok, los geht's. Ich zitiere mal mein erstes Posting zum besseren Verstaendnis.

        Der Benutzer hat 12 Moeglichkeiten, Mehrfachauszeichnungen anzugeben, naemlich:

        6 dreifache:
         _/*,  _*/,  */_ ,  *_/,  /*_ ,  /_*,

        und 6 doppelte:
         /*,  */,  /_ ,  _/,  *_,   _*,

        Du setzt eine Prioritaet in der Reihenfolge zB. _*/, dann entfallen aus den dreifachen
        _*/ und aus den doppelten _/, */ und _*

        Du definierst eine Standardreihenfolge fuer diese Mehrfachauszeichnungen, sagen wir erst unterstrichen, dann fett, dann kursiv. Die Zeichfolgen _*/, _/ und _* entsprechen diesem Kriterium, muessen also nicht weiter beruecksichtigt werden. Alle anderen Kombinationen wandelst du mit Hilfe eines regulaeren Ausdrucks in den Standard um.
        Beispiel:
          _/*WORT_/* => beide Auszeichnungen entsprechen dem Standard
          _*/WORT_/* => der erste Teil hat die falsche Reihenfolge, der zweite ist ok.
          _*/WORT_*/ => beide Auszeichnungen haben die falsche Reihenfolge
        usw, das klappt natuerlich auch mit doppelten Auszeichnungen.

        Jetzt brauchst Du eine Funktion, die alle falschen Reihenfolgen durch richtige ersetzt. Die muesste so aussehen:

        function in_standard_umwandeln($originaltext)
        {
          //ersetze im gesamten Text alle Vorkommen von */_  oder  *_/ oder  /*_ (usw.)  durch _*/
          $neuertext = preg_replace("/\/_|\_/|/\*_/", '_*/, $originaltext);
          //ersetze im gesamten Text alle Vorkommen von /_  durch _/
          $neuertext = str_replace('*_', '_/', $neuertext);
          //ersetze im gesamten Text alle Vorkommen von *_  durch _/
          //...da fehlen noch ein paar Moeglichkeiten
          return $neuertext;
        }
        Jetzt hast Du alle Auszeichnungen in der richtigen Reihenfolge und damit kannst Du ja umgehen.

        Ich hoffe, das ist jetzt etwas klarer, wenn nicht, frag ruhig noch mal nach.

        Gruß,

        Dieter

        1. Hi Dieter

          _/*WORT_/* => beide Auszeichnungen entsprechen dem Standard
            _*/WORT_/* => der erste Teil hat die falsche Reihenfolge, der zweite ist ok.
            _*/WORT_*/ => beide Auszeichnungen haben die falsche Reihenfolge

          Vielleicht verstehst du mein Problem falsch. Wenn ein bestimmter Teil mehrere Auszeichnungen hat, tritt das problem nicht auf. Sondern dann, wenn sich verschiedene Auszeichnungen überschneiden. Also so:

          /_wort1 *wort2/ wort3* wort4_

          Per Standard ersetzen komme ich auf:
          <i><u>wort1 <b>wort2</i> wort3</b> wort4</u>

          Ich möchte aber
          <i><u>wort1 <b>wort2</b></u></i> <u><b>wort3</b> wort4</u>

          Die einzige Lösung, die mir im Moment einfällt (vielleicht ist das auch wirklich das Beste) ist, einfach immer jedes Wort mit den Entsprechenden Tags zu versehen. Und dann alle nicht benötigten herauszuschneiden:

          1. Schritt:
          <i><u>wort1</u></i> <i><u><b>wort2</b></u></i> <u><b>wort3</b></u> <u>wort4</u>

          Jetzt schneide ich </i><i>, </b><b> und </u><u> heraus und komme auf:

          <i><u>wort1 <b>wort2</b></u></i> <u><b>wort3</b> wort4</u>

          Tatsächlich, hat geklappt zumindest in der Theorie, jetzt also auf in die Praxis

          Danke für den Denkanstoß

          Heizer

          1. Hallo Heizer,

            Vielleicht verstehst du mein Problem falsch. Wenn ein bestimmter Teil mehrere Auszeichnungen hat, tritt das problem nicht auf. Sondern dann, wenn sich verschiedene Auszeichnungen überschneiden. Also so:

            /_wort1 *wort2/ wort3* wort4_

            Du hast recht, ich hatte das missverstanden

            Die einzige Lösung, die mir im Moment einfällt (vielleicht ist das auch wirklich das Beste) ist, einfach immer jedes Wort mit den Entsprechenden Tags zu versehen. Und dann alle nicht benötigten herauszuschneiden:

            Ich finde deine Loesung ziemlich clever auf den ersten Blick, moechte aber zu bedenken geben, dass der User vielleicht sowas abliefert "/_wort1      *wort2/     wort3*   wort4_", also mit mehr Whitespaces als erwartet. Dann wird Deine Loesung, die "</i> <i>" usw zu eliminieren nicht klappen. Insofern wuerde ich erstmal mit einem preg_replace("/ +/", ' ', $text) in dieser Hinicht fuer Vereinheitlichung sorgen.

            Danke für den Denkanstoß

            Manchmal kommt man halt eben auch durch Missverstaendnisse weiter.

            Gruß,

            Dieter

            1. So, jetzt mein bisheriges Ergebnis, am besten in einen Editor mit Syntaxhighlighting kopieren

              // Diese Funktion benütze ich gerne in Verbindung mit PREG_OFFSET_CAPTURE
              function offsetArray($matches) {
                $ret = array();
                while (count($matches) > 0) {
                  $ret[] = array_reverse(array_shift($matches));
                }
                return $ret;
              }

              |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
              $textMarker = array("_" => "u","*" => "b","/"=>"i");

              $testStr = "wort _/wort1 *wo_rt2/ wort3* wort4_      wort";

              echo htmlentities($testStr)."<br />";

              foreach ($textMarker as $marker => $tag) {
                $marker = "\".$marker;
                $markers = "X|\".join("|\",array_keys($textMarker));

              // Hier werden ersteinmal alle Marker durch ein XX-(START|END):(u|b|i)-XX ersetzt, da ich sonst Schwierigkeiten mit dem Slash bei den Schließenden Tags bekommen habe, aber das soll sich noch ändern.
                preg_match_all("/(^|>|$markers|\s)$marker(.+)$marker(\s|$markers|<|$)/uU",$testStr,$matches,PREG_OFFSET_CAPTURE);
                list($orig,$pre,$cont,$post) = offsetArray($matches);
                foreach ($orig as $key => $foo) {
                  $cont[$key][0] = "XX-START:$tag-XX".join("XX-END:$tag-XX XX-START:$tag-XX",explode(" ",$cont[$key][0]))."XX-END:$tag-XX";
                  $testStr = substr_replace($testStr,$pre[$key][0].$cont[$key][0].$post[$key][0],$orig[$key][1],strlen($orig[$key][0]));
                }
              }
              // Hier werden dann die eigentlichen Tags eingesetzt
              $testStr = preg_replace("/XX-START:(u|b|i)-XX/","<\1>",$testStr);
              $testStr = preg_replace("/XX-END:(u|b|i)-XX/","</\1>",$testStr);

              echo htmlentities($testStr)."<br />";

              // dieser Teil kommt mir so vor, als wenn man ihn verkürzen könnte, aber ich weiß im Moment nicht wie. Vielleicht einen Tip? Es werden alle Tags in die gleiche Reihenfolge gebracht. Vorher funktioniert das nicht, da im ersten Schritt die Tags immer um das Wort herumgeschrieben werden.
              $testStr = preg_replace("/(<u><b><i>|<u><i><b>|<b><i><u>|<i><b><u>|<b><u><i>)/","<i><u><b>",$testStr);
              $testStr = str_replace("<b><i>","<i><b>",$testStr);
              $testStr = str_replace("<b><u>","<u><b>",$testStr);
              $testStr = str_replace("<u><i>","<i><u>",$testStr);
              $testStr = preg_replace("/(</u></b></i>|</u></i></b>|</b></i></u>|</i></b></u>|</i></u></b>)/","</b></u></i>",$testStr);
              $testStr = str_replace("</i></b>","</b></i>",$testStr);
              $testStr = str_replace("</u></b>","</b></u>",$testStr);
              $testStr = str_replace("</i></u>","</u></i>",$testStr);

              // Hier werden <x></x> Tags herausgeschnitten. Dazwischen darf beliebiger Whitespace sein.
              foreach ($textMarker as $key => $elem) {
               $testStr = preg_replace('/</'.$elem.'>(\s*)<'.$elem.'>/',"\1",$testStr);
               echo htmlentities($testStr)."<br />";
              }
              echo htmlentities($testStr)."<br />";
              echo $testStr."<br />";

              Das einzige, was jetzt noch stört ist, daß zwischen den Unterstreichungen ein Leerzeichen ohne Unterstreichung ist. Aber das schaffe ich jetzt auch noch, nachdem ich mich seit einem Monat mit dem Gedanken an dieses Script plage.

              Mein ergebenster Dank an dieses Forum, dies war eine der letzten Hürden zu meinem perfekten Content Management System.

              Grüße

              Heizer