[PHP/regex] Links parsen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • xilent_xage
  • Registratie: Februari 2005
  • Laatst online: 15-09 11:35
Hoi,

In mijn framework zit een parser die content uit het CMS hier en daar wat 'verfraait'. Een onderdeel daarvan is een regex die hyperlinks naar downloadbare bestanden op de eigen site voorziet van een extra linkje naar de zipversie van het bestand.

Hiervoor heb ik ooit lang zitten zweten op een goede regex en het volgende voor mekaar geboxt.

code:
1
'/<a href="\/([-A-Z0-9+&@#\/\%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])\.(.*?)\.file"(.*?)>(.*?)<\/a>/si'


Alle betreffende links hebben een relatief pad, en de extentie .file.

Nu heeft dit altijd prima gewerkt, tot vandaag. Iemand gooit een lijstje links in het CMS,en de regex gaat de mist in. De betreffende content:

HTML:
1
2
3
4
5
<ul>
<li><a href="/download1.pdf.file">Download1</a></li>
<li><a href="/download2.pdf.file">Download2</a></li>
<li><a href="/download3.pdf.file">Download3</a></li>
</ul>


en als ik de matches van de regex bekijk zie ik:
code:
1
2
3
4
5
    [0] => <a href="/download1.pdf.file">Download1</a></li><li><a href="/download2.pdf.file">Download2</a>
    [1] => download1.pdf
    [2] => file">Download1</a></li><li><a href="/download3.pdf
    [3] => 
    [4] => Download3


Hier gaat dus vanalles mis: De sluitende anchor-tag wordt gemist etc. Maar als ik naar de regex kijk zie ik het probleem niet - ziet er gewoon goed uit. En om maar gelijk met de billen bloot te gaan:ik ben GEEN held met regexen :)

Acties:
  • 0 Henk 'm!

  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

Dan maak je 'm Ungreedy. ;)

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Acties:
  • 0 Henk 'm!

Verwijderd

Die . matcht alles, sowieso niet handig hier dus. Binnen het argument kun je beter allesbehalve . en " matchen:

code:
1
'/<a href="\/([-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])\.([^."]+)\.file"(.*?)>(.*?)<\/a>/si'

Acties:
  • 0 Henk 'm!

  • rhodium
  • Registratie: Augustus 2003
  • Laatst online: 20:27
Het zou mij niks verbazen dat er iets als \r\n mist achter de </li>. Dus dat alles op 1 regel staat....

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Dit soort situaties is dus precies waarom er altijd gezegd wordt dat het verschrikkelijk dom is om regex parsing te gebruiken i.c.m. HTML documenten. Als je het zoekdomein nog beperkt tot de inner of outer HTML van één bepaalde tag is het met heel veel werk nog robuust te krijgen, maar bij een globale zoek operatie zoals deze is het echt een heel slecht idee.

Vraag jezelf bijvoorbeeld eens af wat er gebeurt als er een class attribuut aan een link toegevoegd is, of een style attribuut, of whatever ander attribuut behalve een href attribuut. Of wat er gebeurt wanneer er tussen de tag naam en het href attribuut twee spaties staan i.p.v. één. Of wat er gebeurt wanneer de opgegeven HTML malformed is. Etc.

Waarom genereer je die extra links niet gewoon clientside m.b.v. wat JavaScript? Het is niet alsof dat dramatisch slechtere accessibility tot gevolg heeft, aangezien altijd nog het originele file als harde link in de HTML staat. De DOM API (en libraries zoals jQuery) zijn gebouwd rondom manipulatie van de HTML tree wanneer deze al geparsed is en zijn dan ook bij uitstek geschikt voor dit soort taken.

Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

R4gnax schreef op maandag 08 augustus 2011 @ 11:57:
Dit soort situaties is dus precies waarom er altijd gezegd wordt dat het verschrikkelijk dom is om regex parsing te gebruiken i.c.m. HTML documenten. Als je het zoekdomein nog beperkt tot de inner of outer HTML van één bepaalde tag is het met heel veel werk nog robuust te krijgen, maar bij een globale zoek operatie zoals deze is het echt een heel slecht idee.
Normaal gesproken zou ik dat met je eens zijn maar in dit geval is het probleemdomein toch echt wel klein genoeg om het prima met een regexp te kunnen doen. Domweg ungreedy maken (ofwel een ? ofwel een U extra in je regexp, yay ;)) en je bent er.
Verwijderd schreef op maandag 08 augustus 2011 @ 11:43:
Die . matcht alles, sowieso niet handig hier dus.
Maakt ook niet uit, .* is prima, mits je er een ? achter zet of een U als modifier gebruikt. ;)

[ Voor 15% gewijzigd door NMe op 08-08-2011 13:21 ]

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • xilent_xage
  • Registratie: Februari 2005
  • Laatst online: 15-09 11:35
bedankt! het werkt nu. reden om het serverside ipv clientside op te pakken is SEO - we willen dat de gezipte versies ook geindexeerd worden.

Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

De toegevoegde waarde van binaire bestanden in de index van Google lijkt me redelijk te verwaarlozen eerlijk gezegd. Al is dit opmerking gebaseerd op een vermoeden, geen feitenkennis. :)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Dit is inderdaad een heeeeel specefiek geval waarin regexen wel "kunnen" in HTML omdat:
- Je <a> -tags niet hoort te nesten;
- Je dus _altijd_ ungreedy hoort te zijn (in dit geval);
- Dom parser alleen maar overbordige parsing en searching gaat zitten doen.

Echter let op dat je _normaal_ gesproken html manipulaties met een DOM/XML Parser zou moeten doen. Omdat html elementen nou eenmaal nested zijn en regex niet in staat is dit bij te houden.

Nog een paar tips:
- Als je cms attributen op de link toestaat, dan zou ik er niet vanuit gaan dat deze met het href attribuut begint;
- Je kunt ook zeggen dat bepaalde karakters niet mogen (hiervoor is het dakje ^):
code:
1
href="/([^"]+).file"

- De / op het begin en eind is je delimiter, hiervoor kan je het best een karakter gebuiken die niet in je regex voorkomt want dan moet je hem escapen. Omdat de slash 3 keer voorkomt had ik voor een ander karakter gekozen. Je mag zelf bepalen welk karakter dit is.

Acties:
  • 0 Henk 'm!

Verwijderd

NMe schreef op maandag 08 augustus 2011 @ 13:19:
[...]

Maakt ook niet uit, .* is prima, mits je er een ? achter zet of een U als modifier gebruikt. ;)
Die ? staat erachter, dus dit argument gaat hier niet op. Ik ga ervan uit dat je het wel kent, maar misschien nuttig om wat te lezen over catastrophic backtracking. ;-)

Anyway, ik bewees erg simpel dat de (.*?) vervangen met ([^".]+) de simpelste oplossing was. De . in de class kan nog weg, maar het belangrijkste is het uitsluiten van de ". Dit voorkomt meteen het probleem dat je ungreedy zou moeten zijn en tegelijkertijd dat je meer zou kunnen matchen dan je wil.
Pagina: 1