• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

[ERLEDIGT] Regular Expression: Nur der letzte Teil des Strings wird gematched

FIR

Neues Mitglied
Hallo html.de-Community,

ich habe nun seit einiger Zeit ein mich ziemlich nervendes Problem und nun die Hoffnung, dass ihr mir hier vielleicht weiterhelfen könnt.

Problem:
Ich habe einen regulären Ausdruck:
PHP:
~^[/]{0,1}[\w\-]+(\[/:[\w\-]+\])*$~
und dazu einen String, der gefiltert werden soll:
PHP:
/news[/:id][/:action][/:user]

Das Problem ist nun, dass er mir im $matches-Array nur zwei Einträge zurückgibt:
PHP:
[0] => /news[/:id][/:action][/:user] // Der String als ganzes entspricht also dem pattern
[1] => [/:user] // Aber warum wird nur der letzte Fund zurückgegeben?

Ich bin nun schon einige Zeit dabei, habe gefühlte Millionen von Varianten ausprobiert. Jedoch hat leider nichts geholfen.
 
Werbung:
Einfach gesagt:

Das hier…

PHP:
<?php

$input = '123';

$pattern = '~^([0-9])+$~';

$matches = array();

preg_match($pattern, $input, $matches);

var_dump($matches);

// array(2) {
//  [0] =>
//  string(3) "123"
//  [1] =>
//  string(1) "3"
// }

…funktioniert nun mal so, dass sich der Quantifikator an der Stelle nicht auf die Anzahl möglicher capturing subpatterns bezieht.

Du könntest es beispielsweise so lösen:

PHP:
<?php

$input = '/news[/:id][/:action][/:user]';

$pattern = '~
^
(/?[\w-]+)
((?:\[/:[\w-]+\])*)
$
~x';

$matches = array();

preg_match($pattern, $input, $matches);

var_dump($matches);

var_dump(preg_split('~(?<=\])(?=\[)~', $matches[2]));
 
  • Like
Reaktionen: FIR
Ah, vielen Dank Mermshaus. So funktioniert es.
Ich hätte es gerne mit einem einzigen Pattern hinbekommen, aber wenn das nicht funktioniert, kann man da wohl nichts machen.
Und deine Lösung hat mich auch im Umgang mit Regex etwas geschult, auch dafür ein Dankeschön :D
 
Werbung:
Habe noch die Doku dazu gefunden:

When a capturing subpattern is repeated, the value captured is the substring that matched the final iteration. For example, after (tweedle[dume]{3}\s*)+ has matched "tweedledum tweedledee" the value of the captured substring is "tweedledee". However, if there are nested capturing subpatterns, the corresponding captured values may have been set in previous iterations. For example, after /(a|(b))+/ matches "aba" the value of the second captured substring is "b".

- http://www.php.net/manual/en/regexp.reference.repetition.php

Das hier scheint auch interessant:

- http://www.regular-expressions.info/captureall.html

Also, ich will nicht ausschließen, dass es irgendeinen Weg mit preg_match_all oder preg_split oder so oder irgendeinem „esoterischen“ Regex-Feature gibt, die gesamte Sache in einem Pattern zu erledigen. Das Problem, auf das ich dabei aber schnell gestoßen bin, ist es, die Validierung der Gesamteingabe zu behalten. Dazu eignet sich etwa preg_match_all mehr oder weniger per Definition nicht, weil es eben die Eingabe nach Matches durchsucht.

Zum Beispiel:

PHP:
<?php

$input = '/news[/:id][/:action][/:user]';

$pattern = '~
(
(?:^/?[\w-]+)
|
(?<!^)(?:\[/:[\w-]+\]+)
)
~x';

$matches = array();

preg_match_all($pattern, $input, $matches);

var_dump($matches);

Das liefert zwar die Infos, aber es liefert sie auch bei:

PHP:
$input = 'foo /news[/:id][/:action][/:user] bar';

Auch im Sinne von KISS würde ich ruhig eine „triviale“ Lösung wählen, die mehrere Schritte braucht, aber besser lesbar ist.
 
Mh.. daran habe ich gar nicht gedacht. Wobei der von dir beschriebene Fall wohl keine Auswirkungen auf den restlichen Ablauf bei mir haben würde.
Und was die Lesbarkeit angeht, hast du ohnehin absolut Recht.
 
Zurück
Oben