Felix Riesterer: wie eine backreference modifizieren?

Liebe Selfer,

ich verwende in PHP die preg_replace()-Funktion. Nun möchte ich, dass in der Ersetzung der Wert aus einem gefundenen Subpattern (ich rede von einer Backreference) in modifizierter Form ersetzt wird. Ein Beispiel:

Aus "Datum 12" soll "Datum 2", oder aus "Datum 469" soll "Datum 5" werden.
Es soll also die gesamte Zahl durch die erste gefundene und um eins erhöhte Ziffer ersetzt werden. Ich dachte da an folgenden Code:

$usereingabe = "Datum 12";
$modifiziert = preg_replace("/Datum (\d)+/", (abs(substr(\1, 0, 1))+1), $usereingabe);

Der obige Code funktioniert nicht. Es wird "\1" innerhalb der geschachtelten Funktionen nicht mehr als Backreference erkannt, daher auch nicht als String zerlegt, um dann als Wert um eins erhöht wieder zurückgeschrieben zu werden.
Der Code gibt jedesmal "Datum 2" aus, da "\1" absolut als "1" erkannt wird, egal was als Subpattern erkannt wurde.

Wer weiß Rat?

Liebe Grüße aus Ellwangen,

Felix Riesterer.

  1. 你好 Felix,

    [...]
    $usereingabe = "Datum 12";
    $modifiziert = preg_replace("/Datum (\d)+/", (abs(substr(\1, 0, 1))+1), $usereingabe);

    Richtig:

    $modifiziert = preg_replace('/Datum (\d)\d*/e',"\1+1",$usereingabe);

    再见,
     CK

    --
    So, wie ein Teil ist, ist das Ganze.
    http://wwwtech.de/
    1. Lieber Christian,

      ich danke Dir von Herzen. Werde mir das gleich "reinziehen".

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

  2. Hallo Felix,

    ich verwende in PHP die preg_replace()-Funktion. Nun möchte ich, dass in der Ersetzung der Wert aus einem gefundenen Subpattern (ich rede von einer Backreference) in modifizierter Form ersetzt wird. Ein Beispiel:

    Aus "Datum 12" soll "Datum 2", oder aus "Datum 469" soll "Datum 5" werden.
    Es soll also die gesamte Zahl durch die erste gefundene und um eins erhöhte Ziffer ersetzt werden. Ich dachte da an folgenden Code:

    $usereingabe = "Datum 12";
    $modifiziert = preg_replace("/Datum (\d)+/", (abs(substr(\1, 0, 1))+1), $usereingabe);

    Du suchst den e-Modifier:

    $modifiziert = preg_replace("/Datum (\d)+/e", "'Datum '.(abs(substr('\1', 0, 1))+1)", $usereingabe);

    (oder so ähnlich, ungetestet)

    So, wie Du es versucht hast, würde der Code (abs...)  schon bevor preg_replace aufgerufen wird, ausgewertet. Wenn Du den Code jedoch in eine Zeichenkette setzt (wichtig) und dann preg_replace den e-Modifier (für "_E_xecute" oder "_E_val" - such Dir was aus) mitgibst, dann wird der Code bei jeder Ersetzung ausgeführt.

    Siehe dazu auch das PHP-Handbuch.

    Viele Grüße,
    Christian

    1. Lieber Christian,

      das mit dem e-Modifier muss ich in der Doku auf http://www.php.net glatt übersehen haben. Herzlichen Dank. Das hilft mir weiter!

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

  3. Liebe Selfer, liebe Christians,

    ich scheitere immer noch. In meinem Beispiel von oben war ich zu stark vereinfachend. Ich verwende diese Funktion, um aus einer Form von [bb]-Code (selbstgestrickt) gültige HTML-Tags herzustellen. Jetzt die volle Wahrheit ;-) :
    [Überschrift1:Überschriften-Text] soll werden zu <h2>Überschriftentext</h2>
    oder
    [überschrift268:Headline] soll werden zu <h3>Headline</h3>

    Dabei soll das Wort "Überschrift" vor dem Doppelpunkt case-insensitive sein, die Zahlenangabe mehrstellig möglich, alles nach dem Doppelpunkt soll auch möglich sein (denn es ist schon durch htmlentities() gegangen).

    Meine echte Codezeile sieht im Moment so aus:
    $quelltext = preg_replace("/[(?:(?i)&uuml;berschrift)(\d)\d*:([^]]*)]/e", "<h\1+1>\2</h\1+1)>\r\n", $quelltext);

    Ich bekomme aber nur eine PHP-Fehlermeldung, da das Script wohl versucht aus meinem ersetzten Krams schlau zu werden, was ja nixht funktionieren kann, da kein gültiger PHP-Code entsteht, bzw. auch kein gültiges Suchmuster.

    Ich bleibe weiter dran. Wenn ihr was neues wißt, würde mich das sher freuen.

    Liebe Grüße aus Ellwangen,

    Felix Riesterer.

    1. Liebe Christians,

      habe nun alle Eure Hinweise und Tipps ausprobiert und komme zu folgendem Ergebnis:

      $quelltext = preg_replace("/[(?:(?i)&uuml;berschrift)(\d)\d*:([^]]*)]/e", "'<h'.(abs('\1')+1).'>\2</h'.(abs('\1')+1).'>\r\n'", $quelltext);

      Damit "funzt" es wunderbar!

      Liebe Grüße aus Ellwangen,

      Felix Riesterer.

    2. Lieber Felix

      Ich verwende für diese Fälle immer folgendes:

      preg_match_all($regExp,$in,$matches,PREG_OFFSET_CAPTURE);

      Da bekommst du in $matches ein wunderbares mehrdimensionales Array, das du dann nach Herzenslust bearbeiten kannst. Wichtig ist nur, das du jedes in $matches vorhandene Array durch array_reverse() schickst, sonst stimmen die Positionsangaben nicht mehr.

      Das eigentliche ersetzen sieht dann so aus:

      $in = substr_replace($in,$repl,$matches[$x][1],strlen($matches[$x][0]));

      Hoffe geholfen zu haben

      Heizer

      1. Lieber Heizer,

        danke für deine Denkanstöße. Dein Beispiel ist mir noch etwas zu kompliziert. Ich habe es noch nicht verstanden. Könnte es folgende Aufgabe lösen, die mein Script nicht schafft?

        <?php
        $usereingabe = "scherz-a";
        $array = array("a" => "frage", "b" => "bold", "c" => "keks");
        $modifiziert = preg_replace("/scherz-([abc])/e", "'Scherz'.$array['\1'].'!'", $usereingabe);
        echo "$usereingabe bedeutet: $modifiziert";
        ?>

        Dieses Script funktioniert nicht. Der Parser beschwert sich jedesmal. Anscheinend ist an dieser Stelle ein Array nicht zulässig...

        Liebe Grüße aus Ellwangen,

        Felix Riesterer.

        1. LIeber Felix

          Mit PREG_OFFSET_CAPTURE bekommst du ein Array dieser Art:

          Array(
            [0] => Array(
               [0] => scherz-a
               [1] => 0
            )
            [1] => Array(
               [0] => a
               [1] => 7
            )
          )

          Die erste Dimension sind die Subpattern, in der zweiten liegt auf Schlüssel 0 das Match und auf Schlüssel 1 die Position im Input-String (also z.B. 7 für das a). Diese Angaben kannst du in substr_replace() verwenden, um genau diesen gefundenen Teil durch etwas beliebiges anderes zu ersetzen.

          in deinem Beispiel würde das dann so aussehen:

          preg_match_all("/scherz-([abc])/",$usereingabe,$matches,PREG_OFFSET_CAPTURE);
          list($orig,$sub1) = offsetArray($matches); // offsetArray() ist von mir, siehe untern.
          foreach ($orig as $key => $elem) {
            $usereingabe = substr_replace($usereingabe,$array[$sub1[$key][0]],$orig[$key][1],strlen($orig[$key][0]));
          }

          Das ist jetzt ungetestet.

          Für mich hat es den vorteil, daß ich sehr flexiebel bin, und die Funktion ist wesentlich übersichtlicher.

          // offsetArray() macht ein array_reverse() für jedes Subpattern.
          function offsetArray($matches) {
           $ret = array();
           while (count($matches) > 0) {
            $ret[] = array_reverse(array_shift($matches));
           }
           return $ret;
          }

          Grüße

          Heizer