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?