Toon posts:

Universele manier om tags uit te lezen?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Universele manier om tags uit te lezen?

Ik vroeg mij af of er een standaard regex is om UBB / eigen tags uit te lezen; een paar voorbeelden:
code:
1
2
[img src="plaatje.gif" title="mijn bal"]
[p class="left"]dit is de tekst[/p]


In al deze gevallen wil ik een resultaat dat er zo uit ziet:

code:
1
2
3
4
5
6
7
8
9
Array (
  src => plaatje.gif
  title => mijn bal
)

Array (
  class => left
  inner => dit is de tekst
)

Omdat velen hier al eens een eigen forum / berichtenbord / cms gemaakt hebben, is er waarschijnlijk een standaard oplossing hiervoor. Kan iemand mij op weg helpen? Volgens mij kan het allemaal met een regex, maar mijn kennis daarvan is erg zwak.

[ Voor 9% gewijzigd door Verwijderd op 01-12-2008 00:15 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op zondag 30 november 2008 @ 21:41:
Volgens mij kan het allemaal met een regex, maar mijn kennis daarvan is erg zwak.
Dan moet je je verdiepen in de materie ;) Zie bijv. hier.

Daarbij wil je dit waarschijnlijk niet oplossen met een regex maar met een stack based parser.
Some people, when confronted with a problem, think "I know, I’ll use regular expressions." Now they have two problems. --"Jamie Zawinski
:Y)

[ Voor 15% gewijzigd door RobIII op 30-11-2008 21:50 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 22:11

Creepy

Tactical Espionage Splatterer

O-)
offtopic:
Aka, ik had hem al in m'n sig staan voor de post van RIII ;)

[ Voor 21% gewijzigd door Creepy op 30-11-2008 21:56 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Een stackbased parser zou echt overkill zijn. Ik wil geen echt UBB parser, maar dat was naar mijn idee een makkelijke vergelijking. In werkelijkheid wil ik gebruikers van mijn website de mogelijkheid geven om dynamische content te kunnen schrijven (php code wordt niet geaccepteerd):
code:
1
2
[time syntax="Y:m:d"] // wordt serverside vervangen door php time output
[quote class="left" text="dit is een quote"] // wordt vervangen door een <blockquote>, bv.

Alle tags zijn in principe van dezelfde vorm (uitbreiden kan altijd nog):
code:
1
[tag_name property1="value1" property2="value2" ... propertyN="valueN"]

Het aantal properties is in principe oneindig. Vandaar dat een stackbased parser me wat overkill lijkt, en vandaar de vraag of dit met een regex kan (die ook rekening houdt met als iemand per ongeluk meerdere spaties zet, zie hieronder):
code:
1
[tag_name property="value1"   property2 = "value2"]

Of is het sowieso beter om dit zonder regexen te doen? En zo ja, hoe zou je dat in php moeten doen? Telkens die tag exploden op spaces? Dat kan niet, want wat tussen quotjes staat moet bij elkaar blijven...? Als de oplossing daarvoor weer een "stack based" parser is, dan wil ik daar best meer over leren. Ik heb alleen twee problemen hiermee: 1) ik vind redelijk wat parsers via Google, maar nergens een artikel dat het principe (eenvoudig!) beschrijft en 2) alle parsers die ik vind zijn OO geprogrammeerd, en daar heb ik nog nooit iets mee gedaan. Weet iemand een (hele) eenvoudige niet-OO parser, om het op mijn niveau op te pakken?

[ Voor 15% gewijzigd door Verwijderd op 01-12-2008 00:29 ]


Acties:
  • 0 Henk 'm!

  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

Regex is te traag en te foutgevoelig voor dit soort zaken.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op maandag 01 december 2008 @ 00:22:
Een stackbased parser zou echt overkill zijn.
Want :?
Verwijderd schreef op maandag 01 december 2008 @ 00:22:
(die ook rekening houdt met als iemand per ongeluk meerdere spaties zet, zie hieronder):
code:
1
[tag_name property="value1"   property2 = "value2"]
En wat als iemand per ongeluk quotes vergeet? Of een bracket? Of... Whitespace is misschien de meest obvious fout die mensen wellicht maken vanuit een 'devver' point of view, maar bedenk dat 'users' eerder de andere fouten zullen maken. Juist dan ga je met een regex falikant de mist in (overigens, met een niet erg slimme parser ook :P )
Verwijderd schreef op maandag 01 december 2008 @ 00:22:
Als de oplossing daarvoor weer een "stack based" parser is, dan wil ik daar best meer over leren.
Dan zou ik dat toch doen; een regex maakt het nou ook niet meteen leesbaar(der).
Verwijderd schreef op maandag 01 december 2008 @ 00:22:
Ik heb alleen twee problemen hiermee: 1) ik vind redelijk wat parsers via Google, maar nergens een artikel dat het principe (eenvoudig!) beschrijft en 2) alle parsers die ik vind zijn OO geprogrammeerd, en daar heb ik nog nooit iets mee gedaan. Weet iemand een (hele) eenvoudige niet-OO parser, om het op mijn niveau op te pakken?
Dan heb je meteen een goede aanleiding om OOP onder de knie te krijgen; toch wel een handig 'aspectje' van programmeren waar je later ook profijt van zult hebben en de basics zijn echt geen rocket science.

In een regel: een stack based parser analyseert de tekst en bij ieder item van betekenis zet je dat item op die stack waarbij je, als je de 'sluit tag' tegen komt, het item van de stack popped om het af te handelen. Het is iets gecompliceerder, maar dat is het general idea :P

[ Voor 44% gewijzigd door RobIII op 01-12-2008 00:35 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
RobIII schreef op maandag 01 december 2008 @ 00:30:
In een regel: een stack based parser analyseert de tekst en bij ieder item van betekenis zet je dat item op die stack waarbij je, als je de 'sluit tag' tegen komt, het item van de stack popped om het af te handelen. Het is iets gecompliceerder, maar dat is het general idea :P
Ja, maar er is dus niet altijd een sluittag, bijvoorbeeld bij:
code:
1
[time syntax="Y:m:d"]

Kan dat dan nog wel met een stackbased parser?

En een tweede vraag: hoe kan ik, zonder een regex, bijvoorbeeld onderstaand voorbeeld naar een array krijgen?
code:
1
2
3
4
5
6
7
8
[mytag property1="value1" property2="value 2" ... propertyN="valueN"] // naar

Array (
  tag_name => mytag
  property1 => value1
  property2 => value 2
  //etc...
)

Ik snap namelijk niet hoe ik met reguliere php string functies dat kan bereiken...? Als iemand daar een voorbeeld van zou kunnen geven, heb ik iets om vanuit te gaan :)

[ Voor 34% gewijzigd door Verwijderd op 01-12-2008 01:05 ]


Acties:
  • 0 Henk 'm!

  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

Hoe zou je het met de hand doen? Een stackbased parser werkt grofweg op dezelfde manier ;)

Acties:
  • 0 Henk 'm!

Verwijderd

Volgens mij kan een regex dit niet parsen, omdat de taal niet regulier is. Je wilt evenveel sluittags als opentags, maar dat kan je niet uitdrukken in een reguliere expressie.

Acties:
  • 0 Henk 'm!

  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

Ik heb maar even voor je gegoogled. Check de 1e hit. Aardige uitleg in NL.

Acties:
  • 0 Henk 'm!

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

NMe

Quia Ego Sic Dico.

Verwijderd schreef op maandag 01 december 2008 @ 01:01:
[...]

Ja, maar er is dus niet altijd een sluittag, bijvoorbeeld bij:
code:
1
[time syntax="Y:m:d"]

Kan dat dan nog wel met een stackbased parser?
Waarom zou dat niet kunnen? Een stackbased parser is semi-intelligent en wéét dus wat een tag doet en of die tag een sluittag heeft of niet. :)
Verwijderd schreef op maandag 01 december 2008 @ 01:19:
Volgens mij kan een regex dit niet parsen, omdat de taal niet regulier is. Je wilt evenveel sluittags als opentags, maar dat kan je niet uitdrukken in een reguliere expressie.
Veel dingen kúnnen wel, maar dat wordt zo ingewikkeld dat je met een stackbased parser allang klaar was geweest. Vooral nesting is vaak lastig te realiseren met een regex. :)

[ Voor 35% gewijzigd door NMe op 01-12-2008 01:41 ]

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

  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

BalusC schreef op maandag 01 december 2008 @ 01:30:
Ik heb maar even voor je gegoogled. Check de 1e hit. Aardige uitleg in NL.
Dat kan beter

Modbreak: Leuk, maar zullen we het verder maar serieus houden in dit subforum? :)

[ Voor 27% gewijzigd door NMe op 01-12-2008 03:26 ]

NKCSS - Projects - YouTube


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Lachen, maar daarmee ben ik nog niet geholpen. Zoals ik zei, retourneert zo'n query inderdaad een hoop links waar je OO stackbased parsers kunt downloaden, maar nergens staat een stackbased parser 101 uitleg. De code van die parsers nagaan is voor mij ook erg lastig, want allemaal OO. Voor het geval mensen dan nog twijfelen: ik heb zelf via Google de volgende links bezocht:Maar zoals gezegd - ik moet beginnen met onderstaande tag in een array te stoppen, maar weet niet waarop ik die moet exploden. Kan iemand mij mss. op weg helpen?
code:
1
2
3
4
5
6
7
8
[mytag property1="value1" property2="value 2" ... propertyN="valueN"] // naar

Array (
  tag_name => mytag
  property1 => value1
  property2 => value 2
  //etc...
)

Acties:
  • 0 Henk 'm!

  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

Probeer het eens met

\[(?<tag>[^\s\]]+)(?<props>[^\]]+)

en

(?<prop>[^=\s]+)=("(?<val>[^"]+)|(?<val>[^\s]+))

Update:

Ik zal het even verduidelijken, Ik zou het zelf met Regexes doen, aangezien ik daar best veel ervaring mee heb. Om het lullige linkje goed te maken heb ik iig even deze twee regexes voor je geschreven.

Ik heb ze getest met jouw voorbeeld in de CSharpRegexDemo van www.regular-expressions.info (kan ik aanraden om snel regexes te bouwen/testen).
code:
1
[mytag property1="value1" property2="value 2" ... propertyN="valueN"]

[ Voor 66% gewijzigd door CMG op 01-12-2008 12:58 ]

NKCSS - Projects - YouTube


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 25-09 16:59

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op maandag 01 december 2008 @ 12:02:
[...]

Lachen, maar daarmee ben ik nog niet geholpen. Zoals ik zei, retourneert zo'n query inderdaad een hoop links waar je OO stackbased parsers kunt downloaden, maar nergens staat een stackbased parser 101 uitleg. De code van die parsers nagaan is voor mij ook erg lastig, want allemaal OO. Voor het geval mensen dan nog twijfelen: ik heb zelf via Google de volgende links bezocht:Maar zoals gezegd - ik moet beginnen met onderstaande tag in een array te stoppen, maar weet niet waarop ik die moet exploden. Kan iemand mij mss. op weg helpen?
code:
1
2
3
4
5
6
7
8
[mytag property1="value1" property2="value 2" ... propertyN="valueN"] // naar

Array (
  tag_name => mytag
  property1 => value1
  property2 => value 2
  //etc...
)
Je moet ook niet exploden.....

Een stackbased parser is niks anders dan het teken voor teken doorlezen van je string en bijhouden in welke state je bent. Het 'stack' gedeelte komt pas om de hoek kijken wanneer je die state gaat stapelen in een stack.

Om even naar je simpele voorbeeldje te kijken heb je de stack zelf nauwlijks nodig. Daarvoor werkt het op de volgende manier:

Doordat je een [ hebt gezien is je parser in de 'tag verwerk' state terecht gekomen. Je gaat nu teken voor teken kijken tot je bij whitespace komt. De tekens die tussen de [ en de whitespace staan zijn samen de naam van je tag.

Vervolgens gaat je parser in de state 'properties lezen'. Dat betekend dat hij het eerst volgende niet whitespace teken opzoekt en dan doorleest tot een =. Dit wordt de naam van de property. Vervolgens leest tot de eerste " en begint dan door te lezen tot de volgende " en alles wat daartussen staat is de value van die property. Dit blijf je doen totdat er een ] gevonden wordt. Dan ben je klaar met de tag inlezen.

Wat is er nu stackbased aan? Nou, in dit voorbeeld is de state de gehele actie 'tag verwerken', maar daarbovenop heb je ook de 'lees tagnaam' state en de 'lees property' state en eventueel 'lees propertyvalue' state.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 02:49

.oisyn

Moderator Devschuur®

Demotivational Speaker

Met het risico om er een probleem bij te maken O-), je kunt je tags best opsplitsen middels een regex in de structuur die jij wilt. De code wordt er alleen niet leesbaarder van, en voor je je regex eindelijk goed hebt ben je ook alweer een tijd verder. Je moet dat dus ook maar meer zien als een optimalisatie.

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!

  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

En als je over een paar maanden een bug ontdekt dat in de regex gefixt moet worden ben je alweer een paar uur of dagen :X verder voordat je doorhebt hoe die regex nou werkt :+

Acties:
  • 0 Henk 'm!

Verwijderd

Janoz schreef op maandag 01 december 2008 @ 13:12:
Wat is er nu stackbased aan? Nou, in dit voorbeeld is de state de gehele actie 'tag verwerken', maar daarbovenop heb je ook de 'lees tagnaam' state en de 'lees property' state en eventueel 'lees propertyvalue' state.
En je kan gewoon de stack van functie-aanroepen gebruiken. Dus je hoeft niet zelf expliciet met een stack te werken. Dat wil dus zeggen:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Node contains Text or ComplexTag or SimpleTag
// Text is anything except Tags
// ComplexTag contains child Node ( [tag] child node [/] )
// SimpleTag doesn't contain anything ( [tag] )

Array complexTags;
Array simpleTags;

readNode()
{
    skipWhitespace();

    while(!code.end_of_file() && !contains(code, "[/]"))
    {
        if(contains(code, complexTags))
            readComplexTag();
        else if(contains(code, simpleTags))
            readSimpleTag();
        else
            readChar(); // will be Text
    }
}

readComplexTag()
{
    readNode();
    readEndTag();
}

readSimpleTag()
{
}

Dit is dan even zonder properties in je tags, maar dat kan je er simpel inbouwen. Ook "doet" dit nog niks, maar in elke functie kan je gedrag aan je tags toevoegen. Je wil ook eigenlijk voor elke tag een aparte functie maken, zodat elke UBB-tag van alles kan doen met de Text.

[ Voor 12% gewijzigd door Verwijderd op 01-12-2008 14:22 . Reden: code wat simpeler gemaakt / extra bugs erin zetten :P ]


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 25-09 16:59

Janoz

Moderator Devschuur®

!litemod

Klopt, maar ik was vooral het concept aan het uitleggen. Topicstarter gaf al aan dat hij het uit sourcecode niet op kon maken. Daarnaast geef ik ook nergens dat de callstack niet gebruikt kan worden voor het bijhouden van de stack ;)..

[ Voor 30% gewijzigd door Janoz op 01-12-2008 14:24 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben begonnen met een opzetje zoals ik het me voorstel. Overal in de codebase kunnen macro functies worden gedefinieerd (door prefix mt_). Er komt 1 functie die een string doorzoekt op matches met alle mt_functies, en die daarna deze functies aanroept:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function macrotags_scrape_macros($text) {
  preg_match_all('/ \{ ( [^\{\}]+ )* \} /x', $text, $matches);
  $mt_tag_match = array_unique($matches[1]);
  foreach ($mt_tag_match as $mt) {
    $func_name = (strstr($mt, ' ')) ? substr($mt, 0, strpos($mt, ' ')): $mt;
    if (function_exists("mt_$func_name")) {
      $mt_tags[$func_name] = $mt;
    }
  }
  return (empty($mt_tags)) ? false : $mt_tags;
}

function macrotags_parse_macros($text) {
  $mt = macrotags_scrape_macros($text);
  return macrotags_expand_macros($mt);
}

function macrotags_expand_macros($mt) {
  foreach ($mt as $macro => $properties) {
    // code submitten naar functie
  }
}

function mt_date($param) {
  return date($param['syntax']);
}

function mt_quote($param) {
  return '<blockquote id="'. $param['id'] .'">'. $param['text'] .'</blockquote>';
}

$string = 'Dit is een {date syntax="Y:m"} en een {quote id="1" text="blaat"}';

echo macrotags_parse_macros($string);

In regel 7 maak ik de array aan die alle macro-informatie bevat. Hoe kan ik deze nu vormgeven als:
PHP:
1
$mt_tags[$func_name] = array($property1 => $value1, $property2 => $value2); // etc...

Ben ik zo op de goede weg?

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Dat ligt eraan. Die quote-functie met text is een beetje vreemd. Wil je daarin geen verdere opmaak kunnen doen? Zolang je niet aan nesting doet of zelfs sluittags zal een regexp-oplossing prima werken. Maar in bijvoorbeeld RML kunnen dingen die met een regexp nooit gaan kunnen (tenminste, niet tot in oneindig diep), dus het kan zijn dat je zo een verkeerde weg in slaat. Voorbeeldje:

drie tabellen in elkaar met niet-toepasbare sluittags
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
[table border=1 bordercolor=#000000]
[tr]
[td][table border=1 bordercolor=#000000]
[tr]
[td][table border=1 bordercolor=#000000]
[tr]
[td]drie tabellen in elkaar met niet-toepasbare sluittags [/tr][/table][/td]
[/tr]
[/table][/td]
[/tr]
[/table][/td]
[/tr]
[/table]
[/tr][/table]

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik denk dat er wat verwarring bestaat over wat ik wil maken: ik wil dus geen UBB parser maken die geneste tags kan verwerken, maar een scriptje dat enkel tags van de vorm
code:
1
[tagname property1="value1" property2="value2" ...]

kan omzetten naar
code:
1
2
3
4
5
Array (
  tag => tagname
  property1 => value1
  property2 => value2
)

Het kan natuurlijk ook zijn dat ik het verkeerd begrijp, maar is het nu echt nodig om hier een dikke OO stackbased parser voor te bouwen?

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 02:49

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat heeft "dik OO" er nou weer mee te maken :? En nee, die parser hoeft niet per se stackbased, maar tevens is het alternatief ook niet meteen een regex. Zelf doe ik het overigens wel met 2 regexen, eentje die de tekst opdeelt in gewone tekst en tags, en eentje die van een enkele tag de attributen omzet in een structuur zoals jij 'm wilt hebben.

[ Voor 76% gewijzigd door .oisyn op 01-12-2008 17:17 ]

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!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 25-09 16:59

Janoz

Moderator Devschuur®

!litemod

Mijn eerste stackbased parser heb ik geimplementeerd in pascal. Dat is zo procedureel als het maar kan..

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Wat vinden jullie hier dan van?
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
function parse_tag($text) {
  $values = array();
  if (preg_match_all('/([^=\s]+)=("(?P<value1>[^"]+)"|' . '\'(?P<value2>[^\']+)\'|(?P<value3>.+?)\b)/', $text, $matches, PREG_SET_ORDER))
  foreach ($matches as $match) {
    $values[trim($match[1])] = trim(@$match['value1'] . @$match['value2'] . @$match['value3']);
  }
  return $values;
}

$tag = "foo=dit bar=\"foo bar\" foobar='foobar foo' blaat=boe";

print_r(parse_tag($tag));

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Verwijderd schreef op maandag 01 december 2008 @ 17:14:
kan omzetten naar
code:
1
2
3
4
5
Array (
  tag => tagname
  property1 => value1
  property2 => value2
)
Kijk, hier kan je waarschijnlijk heel mooi een object gebruiken in plaats van een array. In dat object stop je dan apart de tagname en de property->value paren, zodat je mooi onderscheid kan maken.

Maar waarom zou je niet gelijk de uiteindelijke tekst gaan maken en doe je uberhaupt deze tussenstap? Ik bedoel dus met bijvoorbeeld preg_replace_callback.
Die namen maken het niet duidelijker. Waarom trim je iets waar geen whitespace in mag zitten? Het kan ook iets korter, met ingebouwde trim, en nog onbegrijpelijker :) :
PHP:
1
2
3
4
5
6
7
function parse_tag($text) {
    if (preg_match_all('/(\w+)\s*=(?:\s*(["\'])\s*)?(.*?)\s*(?:\2|\W(?!.*\2)|$)/s', 
        $text, $matches))
        return array_combine($matches[1],$matches[3]);
    else
        return array();
}

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
pedorus schreef op maandag 01 december 2008 @ 19:06:
PHP:
1
2
3
4
5
6
7
function parse_tag($text) {
   if (preg_match_all('/([^=\s]+)\s*=(?:\s*(["\']))?\s*(.*?)\s*(?:\2|\W(?!.*\2)|$)/s', 
      $text, $matches))
      return array_combine($matches[1],$matches[3]);
   else
      return array();
}
Thanks! Ik heb nu het volgende. Reden voor de tussenstap is dat elke tag gekoppeld is aan een functie, die overal in de codebase kan staan (zodat ontwikkelaars hun eigen tags kunnen toevoegen). Ik hoor graag commentaar hierop - gevallen waarin de regex niet zou voldoen bijvoorbeeld, of dat eea misschien korter kan:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function mt_foo($properties) {
  print_r($properties);
}

function mt_bar() {
  echo 'bar!';
}

function macrotags_scrape_macros($text) {
  preg_match_all('/ \{ ( [^\{\}]+ )* \} /x', $text, $matches);
  $mt_tag_match = array_unique($matches[1]);
  foreach ($mt_tag_match as $mt) {
    $func_name = (strstr($mt, ' ')) ? substr($mt, 0, strpos($mt, ' ')): $mt;
    if (function_exists("mt_$func_name")) {
      $properties = parse_tag($mt);
      call_user_func("mt_$func_name", $properties);
    }
  }
  return (empty($mt_tags)) ? false : $mt_tags;
}

function parse_tag($text) {
  if (preg_match_all('/([^=\s]+)\s*=(?:\s*(["\']))?\s*(.*?)\s*(?:\2|\W(?!.*\2)|$)/s', $text, $matches)) {
    return array_combine($matches[1],$matches[3]);
  }
  else {
    return array();
  }
}

$tag = "Een stuk tekst met een aantal {bar} tags. Alle 
       {foo foobar='foobar foo' blaat=boe} tags moeten worden geparsed.";

macrotags_scrape_macros($tag);

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Verwijderd schreef op maandag 01 december 2008 @ 19:34:
Ik hoor graag commentaar hierop - gevallen waarin de regex niet zou voldoen bijvoorbeeld, of dat eea misschien korter kan:
Probeer het gewoon eens uit met vage gevallen als
code:
1
{foo !!@#'foo = ":}bar" }

Ik zou zeggen dat dit gezien moet worden als '{foo foo=":}bar"}', of als fout en geen tag (misschien maar beter), maar dat lukt nu nog duidelijk niet. Ik heb mijn regexp ook maar even aangepast, ik had onbewust [^=\s] laten staan. :)
Veel debugplezier! ;)

En verder is array_unique en mt_tags een beetje vreemd, en kan dat splitsen van func_name en de rest ook prima in de regex.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

En wat moet er gebeuren in een geval als dit?
code:
1
[kleur=rood]ik ben rood, [kleur=blauw]ik ben blauw,[/kleur] ik ben wat?[/kleur]

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 02:49

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hij zegt al dat ie geen nesting wilt.

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!

Verwijderd

Topicstarter
pedorus schreef op maandag 01 december 2008 @ 21:34:
[...]

Probeer het gewoon eens uit met vage gevallen als
code:
1
{foo !!@#'foo = ":}bar" }

Ik zou zeggen dat dit gezien moet worden als '{foo foo=":}bar"}', of als fout en geen tag (misschien maar beter), maar dat lukt nu nog duidelijk niet.[...]
Veel debugplezier! ;)
Het aantal mogelijke gevallen in natuurlijk bijna oneindig groot. Hoe maak ik een regex die nooit vastloopt? Is daarom een oplossing zonder regex beter? Bij welke foute syntax trek je de grens?

Acties:
  • 0 Henk 'm!

Verwijderd

.oisyn schreef op maandag 01 december 2008 @ 22:07:
Hij zegt al dat ie geen nesting wilt.
In dat geval wordt [/kleur] dus een equivalent van [kleur=zwart]. Dan gaat het niet meteen fout bij nesting.

[ Voor 26% gewijzigd door Verwijderd op 01-12-2008 22:44 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op maandag 01 december 2008 @ 22:09:
[...]

Het aantal mogelijke gevallen in natuurlijk bijna oneindig groot. Hoe maak ik een regex die nooit vastloopt? Is daarom een oplossing zonder regex beter? Bij welke foute syntax trek je de grens?
* RobIII wijst nogmaals op de eerste reply in dit topic O-)
Je blijft stug volhouden, maar (hoewel regexen best leuk kunnen zijn) regexen zijn niet je oplossing hier en een simpel parsertje is zo geschreven welke al 10x beter werkt dan je regex. Dan gaan gevallen als {foo bar="woot}meuk"} al meteen goed. Maar goed, wees gerust eigenwijs ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
RobIII schreef op maandag 01 december 2008 @ 23:22:
[...]

* RobIII wijst nogmaals op de eerste reply in dit topic O-)
Je blijft stug volhouden, maar (hoewel regexen best leuk kunnen zijn) regexen zijn niet je oplossing hier en een simpel parsertje is zo geschreven welke al 10x beter werkt dan je regex. Dan gaan gevallen als {foo bar="woot}meuk"} al meteen goed. Maar goed, wees gerust eigenwijs ;)
Ik neem best aan dat een parser zonder regex beter werkt, en vooral beter uit te breiden is. Het probleem is alleen dat ik me er helemaal geen voorstelling van kan maken. Onderstaande functie was een eerste verwoede poging om bv. met substr. ipv een regex eea te parsen, maar uiteindelijk heb ik toch ook hier een regex gebruikt:
PHP:
1
2
3
4
5
6
7
8
9
10
11
function macrotags_scrape_macros($text) {
  preg_match_all('/ \{ ( [^\{\}]+ )* \} /x', $text, $matches);
  $mt_tag_match = array_unique($matches[1]);
  foreach ($mt_tag_match as $mt) {
    $func_name = (strstr($mt, ' ')) ? substr($mt, 0, strpos($mt, ' ')): $mt;
    if (function_exists("mt_$func_name")) {
      $mt_tags[$func_name] = $mt;
    }
  }
  return (empty($mt_tags)) ? false : $mt_tags;
}

Vandaar mijn vraag of iemand een opzetje (pseudo)code zou kunnen geven hoe je een string als
code:
1
[tag_name property1="value1" property2="value2"]

verwerkt naar een array als
code:
1
2
3
4
5
Array (
  tag => tag_name
  property1 => value1
  property2 => value2
)

Als iemand mij het begin kan wijzen, weet ik in welke hoek ik moet zoeken om het verder uit te werken.

Acties:
  • 0 Henk 'm!

  • Emmeau
  • Registratie: Mei 2003
  • Niet online

Emmeau

All your UNIX are belong to us

Lees For the 2,295,485th time: DO NOT PARSE HTML WITH REGULAR EXPRESSIONS eens door, en hopelijk zie je dan dat je echt op de verkeerde weg zit.

Quotes toegevoegd zodat .oisyn het ook kan parsen >:)

[ Voor 10% gewijzigd door Emmeau op 02-12-2008 02:24 ]

If you choose to criticise you choose your enemies


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 02:49

.oisyn

Moderator Devschuur®

Demotivational Speaker

For the 738,643th time: PUT URLS CONTAINING COMMAS IN DOUBLE QUOTES IN URL TAGS :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!

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

NMe

Quia Ego Sic Dico.

.oisyn schreef op dinsdag 02 december 2008 @ 02:18:
For the 738,643th time: PUT URLS CONTAINING COMMAS IN DOUBLE QUOTES IN URL TAGS :P
Die fout is wel een mooi voorbeeld waar een non-regex-based parser over het algemeen zichzelf kan bewijzen. Dat een komma tussen quotes een andere betekenis heeft dan wanneer die niet tussen quotes staat krijg je in een regexp ook wel gemaakt, maar simpel gaat dat niet uitzien. :P

@Plaksel: hoe bedoel je dat je geen voorstelling van zo'n parser kunt maken? Janoz in "Universele manier om tags uit te lezen?" legt het best aardig uit. :o

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

Verwijderd

Topicstarter
-NMe- schreef op dinsdag 02 december 2008 @ 02:31:
[...]
@Plaksel: hoe bedoel je dat je geen voorstelling van zo'n parser kunt maken? Janoz in "Universele manier om tags uit te lezen?" legt het best aardig uit. :o
Ja, maar moet ik dan de string exploden met elk karakter? Dus:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$string = 'mijn {string}';

// stap 1: exploden
Array (
  0 => m
  1 => i
  2 => j
  3 => n
  4 =>
  5 => {
  6 => s
  7 => t
  // etc..
)

Acties:
  • 0 Henk 'm!

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

NMe

Quia Ego Sic Dico.

Wacht, ik quote diezelfde post weer even. ;)
Janoz schreef op maandag 01 december 2008 @ 13:12:
[...]

Je moet ook niet exploden.....
;)

Daarnaast heb je ook nog gewoon iets simpels als een loopje waarin je een variabele ophoogt van 0 tot de lengte van de string - 1, om vervolgens het karakter dat op die plaats staat te bekijken (in plaats van die functie kun je strings ook als array benaderen). Afhankelijk van het karakter dat je inleest en de state waarin je parser zich bevindt doe je specifieke dingen met het karakter dat je leest.

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

Verwijderd

Verwijderd schreef op dinsdag 02 december 2008 @ 01:51:
Vandaar mijn vraag of iemand een opzetje (pseudo)code zou kunnen geven hoe je een string als
code:
1
[tag_name property1="value1" property2="value2"]

verwerkt naar een array als
code:
1
2
3
4
5
Array (
  tag => tag_name
  property1 => value1
  property2 => value2
)

Als iemand mij het begin kan wijzen, weet ik in welke hoek ik moet zoeken om het verder uit te werken.
Zoals ik het geleerd heb bij Inleiding Programmeren:
[list=1]
• Hoe ziet je taal er precies uit? Wat is er precies geldig? Wanneer weet je dat een bepaald taal-construct begint, en wanneer weet je dat het opgehouden is? Schrijf dit op in EBNF.
• Vervolgens maak je van elke definitie een functie die precies dat leest, en dan returnt. Dit kan je later optimizen.
• Nu heb je een parser die je taal leest, en daarna klaar is, zonder werkelijk iets gedaan te hebben. Om het af te maken programmeer je dus het gedrag dat elke tag vertoont, in hun respectievelijke functies.

Aan het eind van je BNF zal je altijd losse karakters lezen die samen een groter geheel vormen, dus al je functies eindigen (via aanroepen) altijd in een functie die characters van een string leest. Een functie nextChar() is niet zo lastig te programmeren.

De BNF maken is het lastigste. Het leuke is dat je daarna precies weet hoe de taal in elkaar zit, en het programmeren van de parser is dan vrij snel klaar. Je hebt dan een rock-solid implementatie.

In mijn eerdere code-voorbeeld gaf ik al een kansloze versie van de beschrijving van een taal die alleen simpele [tags] en [tags]met child[/] toestaat. De code eronder is buggy en niet af, maar het was dan ook alleen maar een voorbeeld om een stack-based parser te laten zien. Als je een betere implementatie wilt, zal je dieper moeten nadenken over hoe de taal eruit ziet, tot op de karakters.

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
-NMe- schreef op dinsdag 02 december 2008 @ 02:43:
Wacht, ik quote diezelfde post weer even. ;)
Je kan zelfs nog wel wat verder terug hoor. Ik was even snel aan het zoeken en het blijkt bijvoorbeeld dat Janoz al meer dan 7 jaar geleden een overzichtje van voor- en nadelen van regexp gemaakt ;) Het schrijven van een (UBB-)parser is toch iets dat iedereen wel een keertje doet :)

Hier trouwens een voorbeeldje van een simpele parser van mijn hand.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Oke, dus jullie bedoelen op deze manier?
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function ubb_search_tag($text) {
  $pos = -1;
  $tag = "";
     
  while ($tag == "") {
    $pos1 = strpos($text, "[", $pos1 + 1);
    $pos2 = strpos($text, "]", $pos1);
    
    if ($pos1 == 0 && $pos2 > $pos1) {
      $tag = substr($text, 1, $pos2 - 1);
      $tagtext = $tag;
      $space = strpos($tag, " ");
      $equal = strpos($tag, "=");
      if ($space > -1) $tag = substr($tag, 0, $space);
      if ($equal > -1) $tag = substr($tag, 0, $equal);
      $tag = strtoupper($tag);
      return array("TAG" => $tag, "POS1" => $pos1, "POS2" => $pos2 + 1, "TAGTEXT" => $tagtext);                 
    }
    else if ($pos1 > -1 && $pos2 > $pos1) {
      if (substr($text, $pos - 1, 1) == "\\") {
        $pos += 2;
      }
      else {
        $tag = substr($text, $pos1 + 1, $pos2 - $pos1 - 1);
        $tagtext = $tag;
        $space = strpos($tag, " ");
        $equal = strpos($tag, "=");
        if ($space > -1) $tag = substr($tag, 0, $space);
        if ($equal > -1) $tag = substr($tag, 0, $equal);
        $tag = strtoupper($tag);
        return array("TAG" => $tag, "POS1" => $pos1, "POS2" => $pos2 + 1, "TAGTEXT" => $tagtext);
      }
    }
    else {
      return array("TAG" => "", "POS1" => -1, "POS2" => -1, "TAGTEXT" => "");
    }
  }
}

function ubb_parse($text) {
  $text = str_replace("\r", "", $text);
  $text = trim($text);
  $tag  = ubb_search_tag($text);
  
  while ($tag["TAG"] != "") {
    $tag = ubb_search_tag($text);
    print_r($tag);
  }
}

$text = 'dit is [b class="red"]een[/b] teststring [i]met[/i] [ubb tag="dit" foo="dat"] tags.';
ubb_parse($text);

Ik heb drie problemen met deze code (misschien omdat ik er al lang op zit te prutsen):
  • waarom wordt alleen de eerste [b] tag herkend en niet de volgende [i]?
  • hoe kan ik in 1 functie ook tags retourneren die geen afsluitende [/tag] kennen, zoals [ubb] (wat later een functie wordt om bijvoorbeeld de servertijd op te halen)
  • als ik alle tags kan vinden (problemen hierboven zijn opgelost), hoe dan verder?
Graag tips, commentaar over hoe eea ook efficienter / korter kan :)

Acties:
  • 0 Henk 'm!

  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

Beetje laat, maar toch: probeer nooit \s* te gebruiken, dat is heel erg inefficient en kost een hoop performance. Als je een replace doet op "  " met " " (2x doen), en dan je regex met enkele spaces laten werken is vele male sneller (tenzei je die extra spaces echt nodig hebt :S)

[ Voor 3% gewijzigd door CMG op 02-12-2008 15:15 ]

NKCSS - Projects - YouTube


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op dinsdag 02 december 2008 @ 14:25:
Oke, dus jullie bedoelen op deze manier?
Graag tips, commentaar over hoe eea ook efficienter / korter kan :)
We zitten hier niet om je code te debuggen; dat mag je zelf doen. Daarbij zijn er zat voorbeelden op internet te vinden die korter / efficienter / beter zijn dan wat je nu hebt, maar je vertikt het pertinent je er in te verdiepen omdat het OO is (of zou zijn). Wat verwacht je dan nog van ons :? We blijven onszelf herhalen in dit topic, alleen lijk je nogal selectief te lezen en juist de zaken die er toe doen niet te zien...

Ik heb eens heel even over je code heen gekeken en je blijkt het nog steeds niet te snappen. Je rommelt nu met allerlei strpos zaken om haakjes ( dus [ en ] ) te vinden terwijl het handigst is om gewoon in een loop 1 voor 1 door alle tekens van de string te lopen. Dus beginnen bij pos 0 en eindigen op len(string)-1.

[ Voor 33% gewijzigd door RobIII op 02-12-2008 15:57 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
RobIII schreef op dinsdag 02 december 2008 @ 15:53:
[...]

We zitten hier niet om je code te debuggen; dat mag je zelf doen. Daarbij zijn er zat voorbeelden op internet te vinden die korter / efficienter / beter zijn dan wat je nu hebt, maar je vertikt het pertinent je er in te verdiepen omdat het OO is (of zou zijn). Wat verwacht je dan nog van ons :? We blijven onszelf herhalen in dit topic, alleen lijk je nogal selectief te lezen en juist de zaken die er toe doen niet te zien...

Ik heb eens heel even over je code heen gekeken en je blijkt het nog steeds niet te snappen. Je rommelt nu met allerlei strpos zaken om haakjes ( dus [ en ] ) te vinden terwijl het handigst is om gewoon in een loop 1 voor 1 door alle tekens van de string te lopen. Dus beginnen bij pos 0 en eindigen op len(string)-1.
Dat vind ik dus niet eerlijk. Ik doe ontzettend mijn best om met iets op de proppen te komen waarvan ik eigenlijk nog steeds niet goed snap hoe ik het aan moet pakken. Ik ben van het gebruik van regexen afgestapt, en doorloop nu de $text aan de hand van strpos om idd de tags te zoeken. Ik wil ook best teken voor teken doorlopen - $text als een array benaderen - maar SNAP niet wat ik daar dan mee moet doen. En het klinkt wel leuk, om te zeggen "dan verandert de state van de parser", maar daar kan ik dus niks mee, en zoals gezegd staan er geen tutorials over dit onderwerp online. Dus heb ik een aantal parsers bekeken - allen OO overigens, waaronder die van de PEAR en PHPFreakz, die onder andere onderstaande doen - wat imho niet heel veel anders is als wat ik met mijn functie probeer te doen. Als dat wel zo is, dan zou ik zo graag hebben dat iemand mij uitlegt wat het verschil is, in plaats van mij te beschuldigen van dat ik het pertinent vertik om me erin te verdiepen. Ik doe al twee dagen niet anders, maar ben blijkbaar niet zo slim als alle andere GoT'ers...dat spijt me dan ontzettend.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// PEAR
function _buildTagArray()
{
    $this->_tagArray = array();
    $str = $this->_preparsed;
    $strPos = 0;
    $strLength = strlen($str);

    while ( ($strPos < $strLength) ) {
        $tag = array();
        $openPos = strpos($str, $this->_options['open'], $strPos);
        if ($openPos === false) {
            $openPos = $strLength;
            $nextOpenPos = $strLength;
        }
        if ($openPos + 1 > $strLength) {
            $nextOpenPos = $strLength;
        } else {
            $nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1);
            if ($nextOpenPos === false) {
                $nextOpenPos = $strLength;
            }
        }
        $closePos = strpos($str, $this->_options['close'], $strPos);
        if ($closePos === false) {
            $closePos = $strLength + 1;
        }

        if ( $openPos == $strPos ) {
            if ( ($nextOpenPos < $closePos) ) {
                /* new open tag before closing tag: treat as text */
                $newPos = $nextOpenPos;
                $tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos);
                $tag['type'] = 0;
            } else {
                /* possible valid tag */
                $newPos = $closePos + 1;
                $newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1));
                if ( ($newTag !== false) ) {
                    $tag = $newTag;
                } else {
                /* no valid tag after all */
                    $tag['text'] = substr($str, $strPos, $closePos - $strPos + 1);
                    $tag['type'] = 0;
                }
            }
        } else {
            /* just text */
            $newPos = $openPos;
            $tag['text'] = substr($str, $strPos, $openPos - $strPos);
            $tag['type'] = 0;
        }

        /* join 2 following text elements */
        if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) {
            $tag['text'] = $prev['text'].$tag['text'];
            array_pop($this->_tagArray);
        }

        $this->_tagArray[] = $tag;
        $prev = $tag;
        $strPos = $newPos;
    }
}

// phpfreakz
function parseTag($tag)
{
  $this->tag_full = '['.$tag.']';
  while(strpos($tag, ' =') > 0) $tag = str_replace(' =', '=', $tag);
  while(strpos($tag, '= ') > 0) $tag = str_replace('= ', '=', $tag);
  while(strpos($tag, ', ') > 0) $tag = str_replace(', ', ',', $tag);
  while(strpos($tag, ' ,') > 0) $tag = str_replace(' ,', ',', $tag);
  $exploded = explode(' ', $tag);
  $tag_open = '';
  foreach($exploded as $index => $element)
  {
    $pair = explode('=', $element);
    if(count($pair) == 2)
    {
      $this->parameters[strtolower($pair[0])] = $pair[1];
    }
    if($index == 0) $tag_open = $pair[0];
  }
  $this->tag_open = strtolower($tag_open);
  $this->tag_close = strtolower('[/'.$tag_open.']');
}

/* build($text) generates a tree from $text from where
 * $this is the current root element.
 */

function build($text)
{
  if(empty($text)) return '';

  if(substr($text, 0, 1) == '[')
  {
     /* Starts with an tag?
      * parsing should stop when /tag is found
      *
      * therefor $tag_open, $tag_close should be
      * initialized
      */
    $sclose = strpos($text, ']');
    if($sclose<0)
    {
      $this->append($text);
      return '';
    }
    $tag = substr($text, 1, $sclose-1);

    $text = substr($text, $sclose + 1);
    $this->parseTag($tag);
    if(_quickerUBB_isTextTag(strtolower($tag)))
    {
      $s = strpos(strtolower($text),'[/'.strtolower($tag));
      if($s == false)
        $text = $this->take($text);
      else
      {
        $subtext = substr($text, 0, $s);
        $this->childs[] = $subtext;
        $text = substr($text, $s);
        $text = substr($text, strpos($text,']')+1);
      }
    }
    else
    {
      $text = $this->take($text);
    }
    return $text;
  }
  else
  {
    /* Starts with text, therefor containerobject
     */
    $text = $this->take($text);
    $this->append($text);
  }
}

[ Voor 19% gewijzigd door Verwijderd op 02-12-2008 16:33 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Er zijn trouwens wel parser-generators, zoals bijvoorbeeld Boost::Spirit voor C++. Die voer je een BNF-achtige beschrijving in, en dan ben je klaar. Ik kan alleen geen equivalent vinden voor PHP.

Als je het wilt begrijpen, dan kan je op Wikipedia een aantal artikelen lezen:
Wikipedia: Context-free grammar
http://en.wikipedia.org/w...stic_context-free_grammar « de GoT parser pakt deze url niet :P
Wikipedia: LL parser
Wikipedia: LR parser

Parsers schrijven is trouwens echt niet makkelijk. Het is alleen makkelijker dan een goede regex-implementatie schrijven. Maar dat laatste is echt verdomde moeilijk.

[ Voor 58% gewijzigd door Verwijderd op 02-12-2008 17:05 ]


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
RobIII schreef op dinsdag 02 december 2008 @ 15:53:
Ik heb eens heel even over je code heen gekeken en je blijkt het nog steeds niet te snappen. Je rommelt nu met allerlei strpos zaken om haakjes ( dus [ en ] ) te vinden terwijl het handigst is om gewoon in een loop 1 voor 1 door alle tekens van de string te lopen. Dus beginnen bij pos 0 en eindigen op len(string)-1.
Dit lijkt me onjuist als je bijvoorbeeld de eerstvolgende '[' wil zoeken, omdat geïnterpreteerde PHP-code de neiging heeft om trager te zijn dan een buildin als strpos. Met zo'n loopje herschrijf je in weze strpos... :)
Aan de andere kant is direct naar het eindteken zoeken met strpos een duidelijk teken dat er iets niet begrepen is.
CMG schreef op dinsdag 02 december 2008 @ 15:15:
Beetje laat, maar toch: probeer nooit \s* te gebruiken, dat is heel erg inefficient en kost een hoop performance. Als je een replace doet op " " met " " (2x doen), en dan je regex met enkele spaces laten werken is vele male sneller (tenzei je die extra spaces echt nodig hebt :S)
Ik denk dat je een keer een probleem met backtracking hebt veroorzaakt, dan kan iedere expressie met '*' inderdaad een grote performancehit zijn (lees deze warning (ietsje naar onder scrollen)). \s pakt meer dan alleen spatie, en je wilt ook niet alle dubbele spaties zomaar verwijderen. Dan krijg je van diezelfde gekke code als van phpfreakz:
PHP:
1
  while(strpos($tag, ' =') > 0) $tag = str_replace(' =', '=', $tag);
Ik zie trouwens ook in de PEAR code dingen die beter kunnen ($nextOpenPos onnodig berekenen, gaan zoeken terwijl je al kan weten dat je op een '[' staat, opeenvolgende text-tags maken en later weer combineren, geen support voor '[' of ']' in binnen tags.)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Verwijderd schreef op dinsdag 02 december 2008 @ 16:14:
[...]

Dat vind ik dus niet eerlijk. Ik doe ontzettend mijn best om met iets op de proppen te komen waarvan ik eigenlijk nog steeds niet goed snap hoe ik het aan moet pakken.
No offence, maar jij bent ook niet helemaal 'eerlijk'. Je wilt van alles, maar wilt er geen echte moeite voor doen. Ik wil ook wel een brug ontwerpen, maar ik heb geen zin om daar ook maar iets over te leren. Ook wil ik wel eens proberen om iemand te opereren, maar no-way dat ik een medische opleiding ga volgen, veels te moeilijk!

Ok, dat zijn 2 overdreven voorbeelden, maar het punt is dat je om sommige dingen te kunnen doen iets moet leren. Gewoon een opleiding informatica (hbo,wo) geeft je nagenoeg alle basis kennis die je nodig hebt om deze vraag te beantwoorden. Wil je geen informatica doen (of vergelijkbare zelfstudie) maar toch programmeren, dan kun je het beter bij kant & klare scriptjes houden. Once again, no offence meant ;)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pedorus schreef op dinsdag 02 december 2008 @ 19:20:
Dit lijkt me onjuist als je bijvoorbeeld de eerstvolgende '[' wil zoeken, omdat geïnterpreteerde PHP-code de neiging heeft om trager te zijn dan een buildin als strpos. Met zo'n loopje herschrijf je in weze strpos... :)
Niet helemaal waar natuurlijk en optimaliseren kan altijd nog; het gaat vooralsnog om het principe.
pedorus schreef op dinsdag 02 december 2008 @ 19:20:
Aan de andere kant is direct naar het eindteken zoeken met strpos een duidelijk teken dat er iets niet begrepen is.
Dat is waar ik, impliciet, op doelde.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
flowerp schreef op dinsdag 02 december 2008 @ 21:07:
[...]
No offence, maar jij bent ook niet helemaal 'eerlijk'. Je wilt van alles, maar wilt er geen echte moeite voor doen. Ik wil ook wel een brug ontwerpen, maar ik heb geen zin om daar ook maar iets over te leren. Ook wil ik wel eens proberen om iemand te opereren, maar no-way dat ik een medische opleiding ga volgen, veels te moeilijk!

Ok, dat zijn 2 overdreven voorbeelden, maar het punt is dat je om sommige dingen te kunnen doen iets moet leren. Gewoon een opleiding informatica (hbo,wo) geeft je nagenoeg alle basis kennis die je nodig hebt om deze vraag te beantwoorden. Wil je geen informatica doen (of vergelijkbare zelfstudie) maar toch programmeren, dan kun je het beter bij kant & klare scriptjes houden. Once again, no offence meant ;)
Gut, hoor mij HBO informatica gedaan hebben! Proficiat, maar als je daar mee wilt pochen, zet het dan gewoon in je sig ofzo. Ik vind het knap van jou dat jij blijkbaar in staat bent om voor elk probleem een nieuwe studie af te ronden:
  • belastingformulieren invullen lastig? Ik ga gewoon eerst vier jaar accountancy studeren!
  • waarom start m'n auto nu toch niet? Ik vraag het niemand, want dat is voor losers maar ga nu snel VMBO autotechniek volgen :)
  • ik wil meer genieten van m'n avondeten - ga ik toch even een koksopleiding volgen! Want voor iedereen die dat vertikt moet het maar bij kant-en-klare maaltijden van de Appie houden :(
Wat moet het leven makkelijk zijn als je zo tevreden met jezelf bent _/-\o_ Normale stervelingen, zoals ik, doen slechts 1 studie en zien de rest als hobby en snappen sommige dingen dan niet:
RobIII schreef op woensdag 03 december 2008 @ 00:34:
[...]
Niet helemaal waar natuurlijk en optimaliseren kan altijd nog; het gaat vooralsnog om het principe.
[...]
Dat is waar ik, impliciet, op doelde.
Fijn dat iedereen het met elkaar eens is dat ik iets niet begrepen heb. Ik heb gisteravond wederom een poging gedaan - zal die dadelijk online zetten zodat daar ook weer van gezegd kan worden dat ik het niet begrijp. Als we dat maar lang genoeg doen, MOET ik wel op een bepaald moment een stukje code plakken waarvan iedereen zegt: NU is het goed. Ik kan niet wachten :) Handiger zou natuurlijk zijn om te zeggen WAAROM mijn aanpak van hierboven verkeerd is, maar als we dat zouden doen hebben mensen als flowerp geen plek meer om zich superieur te kunnen voelen. Je moet immers toch iets als je er zo uitziet. No offence. Zouden topics van het niveau als [PHP] Include wordt meerdere keren uitgevoerd* en [PHP] $_POST meegeven in include(); daarom altijd binnen no-time 20+ reacties hebben...zodat we ons met z'n allen topprogrammeur kunnen voelen?

Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Verwijderd schreef op woensdag 03 december 2008 @ 13:47:
Gut, hoor mij HBO informatica gedaan hebben! Proficiat, maar als je daar mee wilt pochen, zet het dan gewoon in je sig ofzo. Ik vind het knap van jou dat jij blijkbaar in staat bent om voor elk probleem een nieuwe studie af te ronden:
  • belastingformulieren invullen lastig? Ik ga gewoon eerst vier jaar accountancy studeren!
  • waarom start m'n auto nu toch niet? Ik vraag het niemand, want dat is voor losers maar ga nu snel VMBO autotechniek volgen :)
  • ik wil meer genieten van m'n avondeten - ga ik toch even een koksopleiding volgen! Want voor iedereen die dat vertikt moet het maar bij kant-en-klare maaltijden van de Appie houden :(
Tsja, als ik voor me zelf wat maaltijden in elkaar wil flansen pak ik een receptenboek en probeer ik wat uit ja. Wil ik een hele gecompliceerde maaltijd maken, dan ga ik niet op een forum lopen 'zeuren' dat het me niet lukt. Als iemand me dan aanraad een of andere cursus te volgen (bijna elk buurthuis biedt wel een kookcursus aan), dan ga ik niet lopen te blaten dat me dat te veel werk is en dat ik gewoon die gecompliceerde maaltijd wil maken.

En voor die auto maken geldt echt hetzelde. Als jij zonder enige voorkennis opeens op een auto forum iets gaat vragen over een intern onderdeel van een motor wat je denkt te moeten vervangen, maar -totaal- geen verstand van sleutelen hebt, en als iemand dan zegt: je moet het motorblok splitsen, waarop jij dan weer zegt dat je dat te moeilijk vind en dat je een makkelijkere oplossing wilt, dan denk ik dat je ongeveer een gelijk antwoord krijgt als hier.

Let op dat ik duidelijk eerder vermelde dat een equivalent ook voldoet, dat wil zeggen de voorgeschreven boeken kun je best ook zelf lezen, maar HBO informatica (en zeker WO) is iets wat je zeker voldoende bagage levert om een vraag als deze op te lossen. Natuurlijk kun je het ook na zo'n studie nog steeds niet helemaal goed voor je zien, maar dan ben je meestal wel in staat om veel gerichter te vragen en niet elke suggestie af te doen met "Dat is te moeilijk. Al dat 'OO-gedoe' snap ik niet. Ik wil iets doen wat eigenlijk best moeilijk is, maar wil me er niet -te- veel in verdiepen'.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

Verwijderd

Het probleem voor mij is dat ik erg weinig ervaring heb met PHP, dus ik kan moeilijk jouw code gaan doorspitten. Ik weet alleen hoe je zo'n parser moet opbouwen, niet hoe je het precies in PHP moet doen. Maar als ik jouw code zie, dan zie ik 1 grote functie. Dat kan haast niet kloppen.

Ik kan wel een klein voorbeeldje geven. Stel je hebt een taal die er zo uitziet:
code:
1
2
3
4
5
tekst      :== [ identifier | whitespace ]*
identifier :== letter, [ letter | cijfer ]*
whitespace :== "alle whitespace karakters"
letter     :== "de karakters a-zA-Z"
cijfer     :== "de karakters 0-9"


Dan zet je dat om in de volgende pseudocode:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
bool einde_van_string_bereikt;
string de_string;
char huidig_karakter;
int i;

/////
// hulp-functies
/////

zet_volgend_karakter_in_huidig_karakter()
{
    i++;
    huidig_karakter = de_string[i];
    if(i >= de_string.length)
    {
        einde_van_string_bereikt = true;
    }
}

isWhitespace(char)
{
    // zegt of de char whitespace is
}

isLetter(char)
{
    // zegt of de char a-zA-Z is
}

isCijfer(char)
{
    // zegt of de char 0-9 is
}

/////
// functies die de taal lezen
/////

leesTekst()
{
    while(!einde_van_string_bereikt)
    {
        if(isWhitespace(huidig_karakter))
        {
            leesWhitespace();
        }
        else if(isLetter(huidig_karakter))
        {
            leesIdentifier();
        }
        else
        {
            error("syntax error: identifier verwacht");
        }
    }
}

leesIdentifier()
{
    leesLetter();

    while(!einde_van_string_bereikt && ! isWhitespace(huidig_karakter)
    {
        if(isLetter(huidig_karakter))
        {
            leesLetter();
        }
        else if(isCijfer(huidig_karakter))
        {
            leesCijfer();
        }
        else
        {
            error("syntax error: geen geldige identifier");
        }
    }
}

leesWhitespace()
{
    if(isWhitespace(huidig_karakter))
    {
        zet_volgend_karakter_in_huidig_karakter();
    }
    else
    {
        error("syntax error: whitespace verwacht");
    }
}

leesLetter()
{
    if(isLetter(huidig_karakter))
    {
        zet_volgend_karakter_in_huidig_karakter();
    }
    else
    {
        error("syntax error: letter verwacht");
    }
}

leesCijfer()
{
    if(isCijfer(huidig_karakter))
    {
        zet_volgend_karakter_in_huidig_karakter();
    }
    else
    {
        error("syntax error: cijfer verwacht");
    }
}

Elke taal-regel heeft een aparte functie. Je kan de gelezen taal op elk van die punten manipuleren door code in de functies toe te voegen. Op dit moment doet het niks anders dan een error geven op het moment dat de taal niet klopt, of, als het wel klopt, zonder iets te doen door de string heen gaan.

Als je de grotere structuren wilt gaan manipuleren, dan zal je de functies van de kleinere structuren wel iets moeten laten returnen, zodat de aangepaste versie in de grotere structuur terechtkomt. Ik zou dat met classes doen (class Tekst, class Identifier, class Whitespace, class Letter, class Cijfer), maar je kan ook simpelweg de string returnen.

Jouw taal (BBcode) zal overigens nooit een error geven, omdat onbekende tags als tekst worden weergegeven. Ik weet niet of je dat met een LL-parser kan doen, aangezien je dan vrij ver vooruit moet kijken (voorgaande code kijkt 1 karakter vooruit (huidig_karakter), en kan op die ene karakter altijd beslissen wat er gedaan moet worden).

Wat je hierboven ziet met de afhandeling van individuele karakters is ook niet echt zinvol, ik zet het er alleen voor de volledigheid bij. Normaalgesproken wil je whitespace gewoon negeren, dus maak je een functie skipWhitespace(), en letters/cijfers kan je gewoon uitlezen, want je weet al welke van de twee het is in het niveau erboven.


Edit, als voorbeeld voor wat je zou kunnen manipuleren:

Je kan met die methodes voor individuele karakters natuurlijk wel logischerwijs dingen doen, zoals de letters allemaal eentje ophogen (a->b, b->c, etc.), de cijfers allemaal eentje lager maken (9->8, 8->7, etc.), en de whitespace allemaal veranderen in 'X'-karakters.


Vervolgens kan je in de leesIdentifier-methode alle karakters van de identifiers omdraaien, behalve als het laatste karakter een cijfer is. Of de identifier omringen met "<" en ">" karakters.

En op het niveau van de leesTekst-methode kan je de identifiers sorteren op alfabetische volgorde.

[ Voor 16% gewijzigd door Verwijderd op 03-12-2008 22:12 ]

Pagina: 1