Ik haat RegExp

Ik ben een deel van gisteren en vandaag bezig geweest met het bouwen van een parser voor een deel van mijn Apache logbestanden. In plaats van via feedburner het aantal RSS-subscribers te laten tellen, wilde ik dat zelf doen.
Er waren een aantal dingen die fout gingen, dat was ook wel weer goed want leerzaam, en het ‘moeten’ gebruiken van Regular Expressions (RegExp) was daar één van. Ehm, waarschuwing vooraf: het is allemaal nogal technisch…
Onhandig logformaat
Om bij het begin te beginnen: ik heb me laten verleiden tot een wat onhandig logformaat. Nadat ik dit artikel gelezen had wilde ik gaan voor een XML-structuur die ik dan met bijvoorbeeld XSLT eenvoudig zou kunnen parsen tot een HTML-pagina.
Ik gebruikte daarvoor een apart logbestand waarin (automatisch) alleen requests voor xml-rss2.php en xml-rss.php worden opgeslagen met deze structuur:

LogFormat “%v%h%{%Y-%m-%d}t %{%T}t%U%q%{User-agent}i” RSS

en dat levert dit soort regels op:

www.gorissen.info195.134.143.1062005-01-07 01:00:30/Pierre/xml-rss2.php?full=yesNIF/1.1 (http://www.newsisfree.com/robot.php users:0)

Er zijn echter twee problemen met dit formaat:
* er is geen ‘root’-element. Elke entry staat weliswaar tussen , maar XML vereist dat er ook een hoofdelement voor het totale bestand is. Dat kun je met een logformaat in Apache niet aangeven.
* er kunnen ongeldige karakters in voor komen. Zie bijvoorbeeld: ?full=yes&catid=8. Elke categorie in mijn weblog heeft een RSS-feed en daardoor kan het gebeuren dat ook bij de RSS-feeds het &-karakter voor kan komen. Maar dat mag niet in XML. Daar moet je & van maken. En dat kan ik ook niet aangeven in Apache.

Handmatig parsen
Nu had ik natuurlijk gewoon het blogformaat kunnen aanpassen in een van de standaardformaten, maar die zijn zover ik kan zien niet eenvoudiger te verwerken.
En daar kwam bij dat ik zeker in het begin dacht ‘dat doe ik wel even’.

Tegen de tijd dat het af is, post ik de complete bestanden wel, nu hou ik het even bij mijn grote liefde: RegExp.

Vraag: Ik hou me ook na vandaag nog aanbevolen voor een goede tutorial voor het gebruik van RegExp in PHP!

Regular Expressions are everywhere
Het eerste gebruik van RegExp was noodzakelijk toen ik er voor wilde zorgen dat de requests voor de RSS-bestanden in een apart logbestand zouden komen.
Daarvoor gebruik ik:

SetEnvIf Request_URI (xml-rss) rss-request

Deze test zet een environmentvariable “rss-request” op “true” als in de naam van het opgevraagde bestand de string “xml-rss” voor komt. Die variabele gebruik ik dan weer om aan te geven dat requests die aan die voorwaarde voldoen in het aparte RSS-logbestand moeten worden weggeschreven:

CustomLog “|X:/Apache/bin/rotatelogs logs/rss%y%m%d.xml.log 86400” RSS env=rss-request

Dit zorgt er overigens ook voor dat er elke dag een nieuwe bestand aangemaakt wordt met namen als rss050105.xml.log, rss050106.xml.log, rss050107.xml.log, etc.

Opsplitsen regels
Voor de eerste stap, het opsplitsen van de regels had ik RegExp kunnen gebruiken, maar dit gaat net zo snel:

$astring = str_replace(““, , $astring);
$logitems = split(““, $astring);

De variabele logitems is een array met per element dit soort strings:

www.gorissen.info195.134.143.1062005-01-07 01:00:30/Pierre/xml-rss2.php?full=yesNIF/1.1 (http://www.newsisfree.com/robot.php users:0)

Van regel naar individuele elementen
Om de verschillende elementen op te splitsen volstaat één RegExp:

$logitem_elements_count = preg_match_all( ‘/<[^>]+>([^<>]*)<\/[^>]+>/’, $logitem, $logitem_elements);

die dan kunnen worden opgeslagen:

$host = $logitem_elements[1][0];
$remote_host_ip = $logitem_elements[1][1];
$remote_host = ;
$date = $logitem_elements[1][2];
$pagina = $logitem_elements[1][3];
$query = $logitem_elements[1][4];
$user_agent = $logitem_elements[1][5];

Bloglines subscribers
Voor de telling van het aantal lezers via RSS had ik nog één RegExp nodig. Als één iemand of vijf personen via Bloglines een abonnement hebben op de RSS-feed, leidt dat namelijk toch maar toch één request van Bloglines per uur.
Maar Bloglines is aardig en geeft in de user-agent aan hoeveel mensen er een abonnement op de feed hebben, bijvoorbeeld:

Bloglines/2.0 (http://www.bloglines.com; 19 subscribers)

Hier wordt dus aangegeven dat er 19 subscribers zijn.

De code die dat aantal uit de user-agent filtert ziet er als volgt uit:

$bloglines_count = preg_match_all( ‘/bloglines.com;[ [:space:] ]([ [:digit:] ]*)[ [:space:] ]subscribers/’, $user_agent, $bloglines_subscribers);
if ($bloglines_count > 0) {
$subscriber_count = $bloglines_subscribers[1][0];
} else {
$subscriber_count = 1;
}

De preg_match_all functie geeft aan hoeveel keer de expressie gevonden is. Is dat één keer (of meer, maar dat komt als het goed is niet voor), dan wordt het gevonden resultaat gebruikt, anders telt de regel gewoon voor één abonnement.

Haat – liefde
Ik neem aan dat als ik zeg: Ik haat RegExp omdat ik ze nog niet doorzie, je na bovenstaande voorbeelden wel een idee hebt van wat ik bedoel. Van de andere kant is het ook zo dat ze ontzettend krachtig zijn en je er in een paar regels code fantastische dingen mee kunt doen. Ik zag op de Wikipedia-pagina over Regular Expressions zojuist ook een lijstje met links staan, onder andere naar een aantal tools. Wellicht was het daar eenvoudiger mee geweest, maar van de andere kant is het zo dat ik ook gewoon een teksteditor gebruik voor het schrijven van PHP. Als je het eenmaal doorziet helpt dat niet zo heel veel meer.

Nu eerst maar eens de benodigde code toevoegen zodat de informatie in MySql wordt opgeslagen.

0 0 stemmen
Bericht waardering
3 Reacties
Inline Feedback
Bekijk alle reacties
xiffy
19 jaren geleden

aangzien je nucleus gebruikt kan je ook de np_referrer gebruiken voor dit statistische gegeven. Op de nucleus wiki heb ik uitgelegd hoe het werkt http://wakka.xiffy.nl/Statistics?v=ggq
Maar je hebt gelijk, bovenstaande excercitie is veel leerzamer en bevredigender. Zelf gedaan!

Michel
19 jaren geleden

In het blad PHP/Architect van maart resp. april 2004 staan 2 artikelen over regexps:
maart 2004 blz 13 ev: "Matchmaker, matchmaker make me a match"
april 2004 blz 15 ev: "Meet Your Match: advanced regular expressions".
Ik heb ze op dit moment alleen als hardcopy beschikbaar, maar eventueel kan ik wel een kopietje organiseren.

Pierre
19 jaren geleden

Ik hou me aanbevolen voor een kopie.