[PHP/PCRE] UBB-parser, string opknippen mbv. reg-ex

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online

Ik heb een stack-based UBB-parser geschreven. Deze werkt goed. Het opknippen van de string in hapklare brokken gebeurt met oa. strpos, substr en strcspn. Ik denk dat hier snelheidswinst is te maken door dit met regex-en te doen. Ik wil niet geheel naar een regex-gebaseerde UBB-parser, ik ken de vele nadelen, maar ik wil regexen gebruiken ter ondersteuning van de stack-based parser.

Ik gebruik een eigen implementatie, kan dus enigszins afwijken van wat je op fora tegen komt.

Ik beschouw een UBB-tekst als een verzameling met daarin 3 soorten strings:

- Een openingstag tussen blokhaken. Eventueel met parameters achter een '='-teken. Gescheiden met komma's. Parameters hebben geen naam. Alleen een bepaalde volgorde dus, maar meestal is er maar 1 parameter. De inhoud van de parameters mag tussen aanhalingstekens (") maar het is niet verplicht. Inhoud mag leeg zijn. Het is bijvoorbeeld zinvol als er blokhaken of komma's in voor komen.
Voorbeelden: [b], [url=www.google.nl], [tag=param1, "param2", param3], [hoi=,,"parameter3"]


Met een backslash aan het einde, vóór de sluitingsblokhaak, is het openingstag en sluitingstag in één, net zoals in xhtml. Voorbeeld: [url="google.nl" /] ipv. [url=www.google.nl]Google![/url]

- De sluitingstag. Blokhaken, achter de openingsblokhaak staat een slash. Achter de slash staat de naam van de tag. Deze mag echter weggelaten worden. Voorbeeld: [/b], [/url], [/]

Openingstag en sluitingstag moeten overeenkomen. [b][i]Tekst[/i][/b] mag, [b][i]Tekst[/i][/i] mag ook, maar [b][i]Tekst[/b][/i] mag niet.

- Gewone tekst. Dus de tekst die niet tussen blokhaken staat, dus tekst die tussen tags staat of helemaal aan het begin of helemaal aan het eind.


Ik wil de UBB-tekst met regex-en in hapklare brokken knippen, om van al dat gedoe met strpos e.d. af te zijn. Ik denk dat één goede regex sneller is dan al die loops vol strpos en substr-statements.

Ik heb de volgende testcode geschreven:
PHP:
1
2
3
4
5
6
   $inhoud='[url=www.google.nl, "param2", param3]   [b]URL wordt ook nog vet gedrukt..';
   echo "Test-string: {$inhoud}<br /><br />\n";
   $pattern='# \[  (  (.+)=     (".*"|.*)     | (.+)         )      \]  #U';
   $pattern=str_replace(' ', '', $pattern); // Spaties verwijderen
   preg_match_all($pattern, $inhoud, $res, PREG_SET_ORDER); // PREG_PATTERN_ORDER of PREG_SET_ORDER
   var_dump($res);


Deze code werkt goed. Ik heb mij alleen nog maar beperkt tot openingstags.
Regel 4 lijkt vrij zinloos, ik gebruik het tijdelijk om in het pattern op regel 3 spaties te kunnen doen voor de leesbaarheid.

Resultaat:
code:
1
2
3
4
5
6
7
8
9
10
11
12
array
  0 => array
      0 => string '[url=www.google.nl]' (length=19)
      1 => string 'url=www.google.nl' (length=17)
      2 => string 'url' (length=3)
      3 => string 'www.google.nl' (length=13)
  1 => array
      0 => string '[b]' (length=3)
      1 => string 'b' (length=1)
      2 => string '' (length=0)
      3 => string '' (length=0)
      4 => string 'b' (length=1)


Ook een tag zoals [test="Dit is een [test]!!!"] wordt dankzij de ondersteuning voor tekst tussen aanhalingstekens prima verwerkt, met als parameter "Dit is een [test]!!!".


Het lukt mij echter niet om ondersteuning voor meerdere parameters (aantal is flexibel) te realiseren. Daarom dit topic.

Stel ik wil de volgende tag parsen: [testtag=param1, "param2", param3]
De code zoals ik hier gepost heb, lijkt in eerste instantie goed te werken, maar heeft twee gebreken:
1. De tags worden niet uitgesplitst. Komen als één parameter in de array. Eventueel kan dit verholpen worden met een tweede regex tijdens het verwerken van de tags.
2. Ondersteuning voor aanhalingstekens functioneert niet. Dat openbaart zich als je bijvoorbeeld "param2" verandert in "[param2]". Het werkt niet, omdat de hele reeks als één parameter zonder aanhalingstekens wordt beschouwd.


Daarom heb ik de volgende regex gemaakt:

PHP:
1
   $pattern='# \[  (  (.+)=    (".*",|.*,)* (".*"|.*)     | (.+)         )      \]  #U';


Een variabel aantal parameters gevolgd door een komma. Uiteraard met aan het eind eentje zonder de komma. Dit werkt niet. Ik krijg de parameters niet afzonderlijk in de array en met aanhalingstekens gaat het wederom fout.

Dat blijkt met deze tag:

code:
1
$inhoud='[testtag=param1, "[param2]", param3]';


De tweede parameter kan alleen succesvol verwerkt worden als de aanhalingstekens er bij worden betrokken, dat gebeurt nu niet. De regex struikelt over de blokhaken in parameter2.

Als ik de regex achter verander in:

PHP:
1
   $pattern='# \[  (  (.+)=    (".*",|.*,) (".*",|.*,) (".*"|.*)     | (.+)         )      \]  #U';


Dan gaat het goed!

Resultaat:
code:
1
2
3
4
5
6
7
8
9
rray
  0 => 
    array
      0 => string '[testtag=param1, "[param2]", param3]' (length=36)
      1 => string 'testtag=param1, "[param2]", param3' (length=34)
      2 => string 'testtag' (length=7)
      3 => string 'param1,' (length=7)
      4 => string ' "[param2]",' (length=12)
      5 => string ' param3' (length=7)


Precies wat ik moet hebben. Ik heb hier het aantal parameters vastgelegd op 3. Maar variabel (zo'n * betekent toch een willekeurig aantal?) werkt dus niet.

Wat kan ik het beste doen?

Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

UBB bestaat net als HTML uit nested elements. Die moet je niet proberen te parsen met een regex, want dat levert alleen maar hoofdpijn op.

Zie bijvoorbeeld hier.

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Bozozo schreef op vrijdag 21 januari 2011 @ 17:28:
UBB bestaat net als HTML uit nested elements. Die moet je niet proberen te parsen met een regex, want dat levert alleen maar hoofdpijn op.
Misschien moet je gewoon even de topicstart lezen. Een regex is prima te gebruiken om de string te tokenizen (en vrijwel elke parser gebruikt regular expressions om de input te tokenizen)

@topicstarter: je moet ook niet te ingewikkeld willen doen. Zo gebruik ik zelf een regex die de tags uit de tekst filtert, en een andere regex om de options uit de tag te filteren. Dat maakt de boel ook een stuk leesbaarder. Zo ondersteun ik bijvoorbeeld, net als GoT, de syntax [url=...] alsmede de syntax [url src=...]. Ik kijk met code eerst gewoon met string code of er na de tag een = staat of niet. Daarna pas ik de één danwel de andere regex toe (voor het matchen van respectievelijk "a,b,c" en "p=a q=b r=c")

[ Voor 37% gewijzigd door .oisyn op 21-01-2011 17:42 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Bozozo
  • Registratie: Januari 2005
  • Laatst online: 20-02 16:10

Bozozo

Your ad here?

Aha. Tja het is ook wel een lange OP :P

Attributen matchen valt wat mij betreft onder parsen en niet onder tokenizen.

TabCinema : NiftySplit


Acties:
  • 0 Henk 'm!

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

NMe

Quia Ego Sic Dico.

.oisyn schreef op vrijdag 21 januari 2011 @ 17:36:
[...]

Misschien moet je gewoon even de topicstart lezen. Een regex is prima te gebruiken om de string te tokenizen (en vrijwel elke parser gebruikt regular expressions om de input te tokenizen)
Dan zou ik echter alsnog liever eerst gewoon matchen op de hele tag, met eventueel nog een regexp om de tag zelf van zijn attributen te scheiden. Vervolgens een explode op de attributen, klaar. :)

'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!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Euh ja dat zeg ik toch in de tweede alinea? Ben ik nou de enige die reacties leest in de topic? :P

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Ja :P

Acties:
  • 0 Henk 'm!

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

NMe

Quia Ego Sic Dico.

.oisyn schreef op vrijdag 21 januari 2011 @ 17:56:
Euh ja dat zeg ik toch in de tweede alinea? Ben ik nou de enige die reacties leest in de topic? :P
Ik maakte in elk geval niet uit je reactie op dat je nog spul met de tag wilde doen zonder regexp. :P Zal wel een interpretatiefoutje geweest zijn van me dan. :+

'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!

  • SvMp
  • Registratie: September 2000
  • Niet online
NMe schreef op vrijdag 21 januari 2011 @ 17:56:
[...]

Dan zou ik echter alsnog liever eerst gewoon matchen op de hele tag, met eventueel nog een regexp om de tag zelf van zijn attributen te scheiden. Vervolgens een explode op de attributen, klaar. :)
Prima, als ik wist hoe.

Hoe maak je een goede expressie voor de gehele tag?

Eindigend met een ] werkt niet, omdat de aanhalingstekens moeten worden ondersteund.

[tag="hoi[hallo]"] wordt opgevat als [tag="hoi[hallo].

[ Voor 5% gewijzigd door SvMp op 21-01-2011 21:10 ]


Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Je maakt het jezelf wel moeilijk. Hoevaak komt het voor dat er een [ of een ] in je ubb attribuut zit. Plus als je dit wilt ondersteunen ben je weer aan het nesten, iets wat je niet met regex gaat redden, omdat je deze situaties krijgt, waarvan ik normaal nog niet eens durf te zeggen wat logisch is:
code:
1
2
3
4
[tag="[tag]"]
[tag="tag]"]
[tag=[tag]]
[tag=[tag]


Ik vraag me nog steeds af wat het grote voordeel is aan een regexp gebruiken. Als je een prima werkende UBB-parser hebt, waarom zou je hem dan 0,002 seconden willen versnellen? Maak gewoon gebruik van caches, zodat je de UBB maar 1 keer hoeft te parsen per post/edit.

Acties:
  • 0 Henk 'm!

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

NMe

Quia Ego Sic Dico.

SvMp schreef op vrijdag 21 januari 2011 @ 21:10:
[...]


Prima, als ik wist hoe.

Hoe maak je een goede expressie voor de gehele tag?

Eindigend met een ] werkt niet, omdat de aanhalingstekens moeten worden ondersteund.

[tag="hoi~[hallo]"] wordt opgevat als [tag="hoi[hallo].
De enige manier om dat fatsoenlijk op te vangen is met een parser. Tenzij er altijd quotes om die [] die niet moeten matchen heen staan, dan kun je eventueel nog wel vooruit zonder, maar anders zit je toch echt al gauw aan een uitgebreidere oplossing vast.

'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!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nee joh, het is vrij gemakkelijk met een regex. Elk teken binnen de [ en ] is óf geen " en geen ], óf een " gevolgd door 0 of meer geen ", gevolgd door een ".

Dus: \\\[([^"\]]|"[^"]*")+\]
Iets uitgebreider als je escape sequences wil supporten.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
.oisyn schreef op zaterdag 22 januari 2011 @ 03:14:
Iets uitgebreider als je escape sequences wil supporten.
Ik had zo'n uitgebreidere op FOK! draaien, maar die liep tegen de backtrack-limiet op. Sindsdien doe ik het tokenizen ook niet meer met een regexp.

Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Dan verplicht je wel quotes bij gebruik van brackets, er onlogisch als je het mij vraagt, maar wat betreft de regex, point taken.

Echter blijft het performance gewin klein. Wat je ook doet, voorkom dat je elke pageload 15 keer moet passen.

[ Voor 22% gewijzigd door ReenL op 22-01-2011 23:23 ]


Acties:
  • 0 Henk 'm!

  • Camulos
  • Registratie: Januari 2009
  • Laatst online: 06-09 22:59

Camulos

Stampert

ReenL schreef op vrijdag 21 januari 2011 @ 23:13:
Je maakt het jezelf wel moeilijk. ... waarvan ik normaal nog niet eens durf te zeggen wat logisch is:
code:
1
2
3
4
[tag="[tag]"]
[tag="tag]"]
[tag=[tag]]
[tag=[tag]
inderdaad ^^ doordat je UBB-tags van te voren al onvoorspelbaarder zijn zul je ook altijd meer moeten reg-exen/checken.

Persoonlijk hou ik ervan om alles simpel te houden :) Mijn UBB-parser is gebaseerd op deze post . Met simpel reg-exen + easy uitbereidbaarheid kun je in principe al een eind komen.

Not just an innocent bystander


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

ReenL schreef op zaterdag 22 januari 2011 @ 22:38:
Dan verplicht je wel quotes bij gebruik van brackets, er onlogisch als je het mij vraagt
Waarom? Als je [url=http://bladiebla.com/iets?a=1]nogiets]link[/url] tegenkomt kun je het onmogelijk goed matchen, want je weet de intentie van de poster niet. Hoort "]nogiets" bij de url, of juist niet en is "nogiets]" onderdeel van de tekst? Kun je nooit weten. Open brackets met sluitbrackets gaan matchen in de parameters van de tag is hetgeen dat onlogisch is, want dat is namelijk nogal afhankelijk van de intentie van de gebruiker zelf. De keuze om [ en ] nested te gaan zitten matchen is volledig arbitrair. Daarom moet je, ook op GoT, quotes gebruiken wanneer je een sluit-bracket als parameter van een tag wilt gebruiken.

[ Voor 11% gewijzigd door .oisyn op 24-01-2011 09:20 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • SvMp
  • Registratie: September 2000
  • Niet online
Precies, en dat geldt niet alleen voor [ en ], maar ook voor komma's en =-tekens. Support voor quotes zijn noodzakelijk. De enige vereenvoudiging op dit punt die nog wel acceptabel is, is het verplichten van de quotes.

Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

In het geval van een URL zou je nog kunnen zeggen dat je 'm dan maar gewoon moet encoden (gezien het feit dat een closing bracket als unsafe wordt beschouwd volgens de RFC), maar da's bijzaak.

Het is in ieder geval belangrijk om het verschil tussen tokenizen en parsen in je achterhoofd te houden als je zo'n ding bouwt, want dat maakt je problemen veel eenvoudiger. Je hebt volgens mij maar 7 tokens nodig, namelijk:
[ul]• [
• ]
• =
• /
• name
• value
• datadan kun je de tokenizer laten besluiten hoe hij values wil lexen. Het maakt voor de parser verder niks uit of iets een gequote string is of niet, want die krijgt gewoon een value token voor de kiezen. Hoe die value token daar komt boeit niet. Als je dus voor jezelf een setje tests op een rijtje zet waarmee de tokenizer goed werkt, dan kun je daarna over parsen na gaan denken. Of andersom, als je een setje tests bedenkt met token sequences kan je later de tokenizer bouwen.

[ Voor 0% gewijzigd door drm op 24-01-2011 23:21 . Reden: 6 != 7 ]

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz

Pagina: 1