PHP regex probleem

Pagina: 1
Acties:

  • Zeezicht
  • Registratie: Juni 2001
  • Laatst online: 08-09 17:59
Om uit een HTML bestand een bepaald gedeelte te filteren gebruik ik het volgende:
eregi("<TAG>(.*)</TAG>", $contents, $out))

In $contents staat het uitgelezen bestand en in $out moet alles wat tussen <TAG> en </TAG> staat komen.
Helaas werkt dit soms wel en soms niet... volgens mij werkt het alleen als het op 1 doorlopende regels staat (dus geen \n).

Wie weet hoe ik het in alle gevallen goed kan doen?

  • Grum
  • Registratie: Juni 2001
  • Niet online
preg_match[_all] + modifiers (zie manual)

  • Zeezicht
  • Registratie: Juni 2001
  • Laatst online: 08-09 17:59
Hiermee kan ik wel (als ik het begrijp tenminste :) ) het gedeelte tussen de tags vervangen door iets...
Maar dat is niet mijn bedoeling... ik wil juist dat stuk eruit vissen voor verdere bewerking.

Misschien kan het met bepaalde modifiers, maar die staan niet echt bij de PHP manual beschreven.

Verwijderd

Als het goed is werkt het zo:
PHP:
1
2
3
<?
eregi("<TAG>(.|\n)+?</TAG>", $contents, $out))
?>

  • Zeezicht
  • Registratie: Juni 2001
  • Laatst online: 08-09 17:59
Op woensdag 10 oktober 2001 15:22 schreef Kyori het volgende:
Als het goed is werkt het zo:
PHP:
1
2
3
<?
eregi("<TAG>(.|\n)+?</TAG>", $contents, $out))
?>
Ik krijg hierop een error:
<b>Warning</b>: REG_BADRPT in <b>./file.php</b> on line <b>57</b><br>

En op regel 57 staat dus de bovenstaande eregi

  • tomato
  • Registratie: November 1999
  • Niet online
Direct antwoord op je vraag:
PHP:
1
2
3
4
<?
eregi("<tag>((.|\n)*)</tag>", $contents, $out);
print_r($out);
?>

Subcapturing 1 wil je nu hebben, je gebruikt de tweede voor je alternation.

Met preg_match_() of preg_match_all() zou dit ook kunnen:
PHP:
1
2
3
4
<?
preg_match("!<tag>(.*)</tag>!is", $contents, $out);
print_r($out);
?>

Standaard matched een . geen newlines. Met de /s modifier wel (de /i modifier gebruik ik om hem case insensitive te maken).

Overigens is er nog iets leuks aan de hand. Wat als er meerdere keren <tag>bla bla</tag> voor komt in je string? Omdat de * greedy is zal hij zo veel mogelijk pakken na de eerste <tag>, zoland de hele regex nog maar matched (tot de laatste </tag> dus).

Hier kun je niet echt veel aan doen als je PHP's POSIX regexen (eregi etc) wilt blijven gebruiken. Ja, als er geen andere tags binnen <tag> en </tag> koppels liggen kun je dit doen:
PHP:
1
2
3
<?
eregi("<tag>([^<]*)</tag>", $contents, $out);
?>

Maar als er wel andere tags binnen kunnen voorkomen heb je een probleem. Met een POSIX gaat je dit gewoon niet lukken. PCRE kent een ungreedy modifier /U. Van individuele quantifiers kun je de greedyness veranderen door er een ? achter te plakken. De volgende PCRE zal dus het eerste <tag>bla bla</tag> matchen want de *? laat de . zo weinig mogelijk matchen zolang er nog maar een totale match nodig is (tot de eerste tag dus):
PHP:
1
2
3
<?
preg_match("!<tag>(.*?)</tag>!is", $contents, $out);
?>

Hoop dat je er iets aan hebt :)

  • Zeezicht
  • Registratie: Juni 2001
  • Laatst online: 08-09 17:59
Het moest uiteindelijk zo worden:
eregi("<TAG>(.*|\n*)</TAG>",$contents, $out);

Dus zonder +? en met de *-tjes bij de . en \n

Toch bedankt voor de hulp want zelf was ik hier nog niet achter gekomen.

  • tomato
  • Registratie: November 1999
  • Niet online
Op woensdag 10 oktober 2001 15:22 schreef Kyori het volgende:
Als het goed is werkt het zo:
PHP:
1
2
3
<?
eregi("<TAG>(.|\n)+?</TAG>", $contents, $out))
?>
POSIX regexen kennen geen ungreedy modifiers. +? is hier dus geen modifier, maar twee quantifiers (+ en ?). Dat verklaart de foutmelding, want twee quantifiers direct achter elkaar is onzin ;)

Ook wilde hij volgens mij ((.|\n)+) (dus met capturing haken) omdat hij de inhoud wilde hebben (+ of * is natuurlijk discutabel, maar het verschil is hier niet echt belangrijk denk ik).

  • Zeezicht
  • Registratie: Juni 2001
  • Laatst online: 08-09 17:59
Bedankt voor het hele verhaal tomato!! Goede uitleg, dat is altijd handig als ik nog eens verder ga regex'en.
Pagina: 1