[PHP] str_ireplace functie vereenvoudigen *

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Ik heb onderstaand script in elkaar gedraaid, wat op een pagina woorden zoekt en vervangt door uitleg (<abbr>). Dit moet uiteindelijk een flinke lijst worden, maar dit is nog om te testen.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$message = "123test saya Saya hamon kassiki tsuba";
$begrip = array(
        "saya",
        "hamon",
        "kassiki",
        "tsuba"
    );

$definitie = array(
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Schede\">saya</abbr></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Patroon van de overvang van gehard staal naar zachter staal op de zijde van het blad\">hamon</abbr></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Punt van de katana\">kassiki</abbr></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Scheidt het handvat van het blad en voorkomt dat het zwaard door de hand glijdt en men met het hand langs het blad glijdt\">tsuba</abbr></span>",
    );

$message = str_ireplace($begrip, $definitie, $message);

echo $message;  
?>


Dit werkt goed, maar er zijn 2 dingen die ik graag zou willen verbeteren:
  1. Bij elke definitie in de array heb ik nu de <span> en <abbr>-tags eromheen staan. Het lijkt me dat dit gemakkelijker kan (waardoor het eenvoudiger uitbreiden wordt).
  2. Als het begrip aan het begin van een zin staat, heeft het hoogstwaarschijnlijk een hoofdletter. Ik heb de hoofdlettergevoeligheid al omzeild door ireplace te gebruiken, maar in de vervanging heeft alles kleine letters. Liefst zou ik de exacte input gebruiken (dus variabele $begrip), maar die wordt dan weer door de replacement gehaald. Hoe kan ik dat voorkomen, of op een andere manier zorgen dat de exacte input terugkomt?
Punt 1 hangt samen met punt 2. Als ik kan voorkomen dat dit gebeuren dubbel vervangen wordt, dan kan ik beide dingen oplossen en heb ik een veel overzichtelijker en makkelijker uit te breiden script. Ik krijg het zelf echter niet voor elkaar, ben er al een paar uur mee aan het stoeien. :?

Iemand een tip om me de goede richting op te sturen?

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 18-08 21:31
Met preg_replace kun je de tweede vraag oplossen.

De eerste kun je doen door eerst de array te maken, en daarna een loop over de array en elke waarde uitbreiden met de tags.

[ Voor 62% gewijzigd door _js_ op 10-03-2008 00:35 ]


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
3. CSS gebruiken. ;)

{signature}


Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
@_JS_: preg_replace heb ik nog niet eerder mee gewerkt, zal het eens opzoeken op PHP.net (maar houd je niet tegen als je hier even een krte uitleg van de syntax wil geven. ;) )
Die loop klinkt eigenlijk vrij logisch.

@Voutloos: Ik gebruik al CSS. Inline CSS weliswaar, maar dat terzijde. 8)
Uiteindelijk komt het natuurlijk de stylesheet terug, maar ik begon met 1 regeltje om te testen en dan is inline natuurlijk het sneltste. Daarna ff snel gekopiëerd om wat meer begrippen te hebben, en dan gaat het er natuurlijk snel zo uitzien. Dat soort dingen beschouw ik echter als het likje verf dat je pas geeft als de constructie staat. Daarvoor geef je alleen ff met een spuitbus het idee. :)

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • Bolukan
  • Registratie: Oktober 2002
  • Laatst online: 23-08 23:43
SFB schreef op zondag 09 maart 2008 @ 18:59:
  1. Bij elke definitie in de array heb ik nu de <span> en <abbr>-tags eromheen staan. Het lijkt me dat dit gemakkelijker kan (waardoor het eenvoudiger uitbreiden wordt).
$Definitie array kaal invoeren en met bewerklusje tags toevoegen?

Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Ben nu aan het stoeien met die preg_replace. Als ik op PHP.net kijk, werkt het ongeveer hetzelfde als str_replace. Ik zie niet hoe ik case-insensitive moet krijgen. Nu kan ik natuurlijk wel elk ding dubbel (met beginhoofdletter en zonder) opnemen, maar dat kon ik ook al met str_replace. Omdat dat echter steeds dubbel werk is (en ik van dingen automatiseren hou) vind ik dat niet handig.

Aangezien preg_replace werd aangeraden en dat niet werkt, doe ik vast iets fout.
Dit is nu mijn code:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$message = "123test saya Saya hamon kassiki tsuba";
$begrip = array(
        '/saya/',
        '/hamon/',
        '/kassiki/',
        '/tsuba/'
    );

$definitie = array(
        "<span style=\"border-bottom: 1px dotted;\"><dfn title=\"Schede\">saya</dfn></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Patroon van de overvang van gehard staal naar zachter staal op de zijde van het blad\">hamon</abbr></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Punt van de katana\">kassiki</abbr></span>",
        "<span style=\"border-bottom: 1px dotted;\"><abbr title=\"Scheidt het handvat van het blad en voorkomt dat het zwaard door de hand glijdt en men met het hand langs het blad glijdt\">tsuba</abbr></span>",
    );

$message = preg_replace($begrip, $definitie, $message, 1);

echo $message;  
?>
 

Wat is daar fout aan?

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • truegrit
  • Registratie: Augustus 2004
  • Laatst online: 19-09 19:31
waarom doe je niet zoals hierboven ook al is vermeld? maak een key->value array aan met als key de woorden en de value de title die je nu in de <abbr> tag zet.

volgens mij doe je dat met array('saya' => 'Schede', etc...);

en dan die loop weer, die ook al is vermeld. Dan kan je zowel de goede case van de letters behouden en het scheelt heel erg in de onderhoudbaarheid!

hallo


Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

SFB schreef op donderdag 13 maart 2008 @ 10:07:
Ben nu aan het stoeien met die preg_replace. Als ik op PHP.net kijk, werkt het ongeveer hetzelfde als str_replace. Ik zie niet hoe ik case-insensitive moet krijgen. Nu kan ik natuurlijk wel elk ding dubbel (met beginhoofdletter en zonder) opnemen, maar dat kon ik ook al met str_replace. Omdat dat echter steeds dubbel werk is (en ik van dingen automatiseren hou) vind ik dat niet handig.
Kijk eens naar regular expression switches. Mogelijk staan voorbeelden hiervan bij andere preg_* functies.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • user109731
  • Registratie: Maart 2004
  • Niet online
En door $0 in de strings in de definitie array te zetten kun je de oorspronkelijke tekst opvragen.

Mooier is hier idd om zoiets te doen:
PHP:
1
2
3
4
5
6
7
8
9
$definitions = array(
  'word' => 'def', 
  'foo' => 'bar',
);

foreach($definitions as $word => $def) {
  $html = '<abbr title="'.$def.'">$0</abbr>';
  // replace $word with $html
}

Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Ben nou alweer een tijdje aan het klooien, maar ik krijg dat met die $0 niet voor elkaar. De output wordt dan
HTML:
1
<abbr title="Schede">$0</abbr>

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • Martin Sturm
  • Registratie: December 1999
  • Laatst online: 18-09 16:47
Volgens mij bedoelt hij in plaats van een $-teken, de backreference operator (in pcre is dat uit m'n hoofd gewoon \(nummer)

In principe zou de code dan volgens mij als volgt moeten zijn:
PHP:
1
2
3
4
5
6
7
8
9
10
11
<?php
$definitions = array(
  'word' => 'def', 
  'foo' => 'bar',
);

foreach($definitions as $word => $def) {
  $html = '/<abbr title="'.$def.'">\1</abbr>/';
  preg_replace("/($word)/i", $html, $message);
}
?>


Dit is niet getest overigens..

Acties:
  • 0 Henk 'm!

  • _js_
  • Registratie: Oktober 2002
  • Laatst online: 18-08 21:31
C:\php>php
<?php
$p = array('/ab/','/cd/');
$r = array('-$0-','=$0=');
echo preg_replace($p,$r,'xxxabxxxcdxxxabxxxcd');
?>
^Z
xxx-ab-xxx=cd=xxx-ab-xxx=cd=
C:\php>


Net getest met arrays en backreferences, en het werkt allemaal zonder problemen, nu moet je er zelf ook wel uitkomen.

Acties:
  • 0 Henk 'm!

  • user109731
  • Registratie: Maart 2004
  • Niet online
SFB schreef op maandag 17 maart 2008 @ 15:55:
Ben nou alweer een tijdje aan het klooien, maar ik krijg dat met die $0 niet voor elkaar. De output wordt dan
HTML:
1
<abbr title="Schede">$0</abbr>
Gebruik je wel preg_replace?
Martin Sturm schreef op maandag 17 maart 2008 @ 16:08:
Volgens mij bedoelt hij in plaats van een $-teken, de backreference operator (in pcre is dat uit m'n hoofd gewoon \(nummer)
\\x == $x. \\0 of $0 matcht de hele string.

Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
De manier van Martin Sturm deed het 'm! Hij was niet helemaal correct, de twee slashes bij de $html waren niet nodig. Dit is 'm geworden:
PHP:
1
2
3
4
5
6
7
8
9
10
<?php
$begrip = array(
    'saya' => 'schede',  
    'kassiki' => 'punt',
);
foreach($begrip as $invoer => $uitleg) {
        $uitleg = '<dfn title="'.$uitleg.'">\1</dfn>';
        $message = preg_replace("/($invoer)/i", $uitleg, $message);
}
?>

Thanks! :D

Ik snap alleen niet waarom er bij de preg_replace "/($word)/i" moet staan. Kun je dat eens uitleggen?

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:44

crisp

Devver

Pixelated

Ik kan het niet laten om een alternatief te posten :P

Allereerst de code:
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
$begrip = array(
    'saya' => 'schede',  
    'kassiki' => 'punt',
    'appel' => 'gezond stuk fruit'
);

$message = 'En met de kassiki van zijn saya doorkliefde hij de appel';

$message = preg_replace(
    '/(?<=[>\s(?]|^)('
        . implode(
            '|',
            array_map(
                create_function(
                    '$s',
                    'return preg_quote($s, "/");'
                ),
                array_keys(
                    $begrip
                )
            )
        )
        . ')(?=[\s.,<)?]|$)/ie',
    '\'<dfn title="\' . htmlspecialchars(\$begrip[strtolower(\'$0\')]) . \'">$0</dfn>\'',
    $message
);

echo $message;


En nu de hamvraag: wat doet ut precies? :P

Wel, het genereert een OR-ed reguliere expressie van alle begrippen. De replacement wordt geevalueerd en haalt de juiste beschrijving op en zorgt tevens voor correcte htmlencoding (niet onbelangrijk). De anonieme functie is noodzakelijk voor regexp-escaping, en verder matched deze enkel 'hele' woorden dmv de look-behind and look-ahead.

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
crisp schreef op maandag 17 maart 2008 @ 22:33:
... en verder matched deze enkel 'hele' woorden dmv de look-behind and look-ahead.
Nice! :)

Ik vraag me enkel af waarom je niet gewoon \b zou gebruiken (word boundary).

Voor performance en duidelijkheid is preg_replace_callback misschien beter.

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Laatst online: 00:44

crisp

Devver

Pixelated

pedorus schreef op maandag 17 maart 2008 @ 23:24:
[...]

Nice! :)

Ik vraag me enkel af waarom je niet gewoon \b zou gebruiken (word boundary).
Omdat je wellicht ook wilt kunnen matchen op teken-reeksen die non-alfanumerieke karakters bevatten - dit concept is bijvoorbeeld ook bijzonder bruikbaar voor het vervangen van bepaalde tekens door smilies ;)
Voor performance en duidelijkheid is preg_replace_callback misschien beter.
True, maar je zal dan wel moeten zorgen dat je 'begrippenlijst' binnen 2 functies beschikbaar is.

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • Martin Sturm
  • Registratie: December 1999
  • Laatst online: 18-09 16:47
Nu zou je wel een discussie kunnen beginnen welke code eenvoudiger te lezen is en dus onderhoudbaarder is....
Maar ik moet zeggen dat Crisp's oplossing wel laat zien dat hij goed is in regular expressions en PHP-magic :P
Het is ook wel een beetje een Perl-achtige manier om dit probleem op te lossen :)

[ Voor 15% gewijzigd door Martin Sturm op 18-03-2008 11:07 ]


Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Crisp's oplossing ziet er heel mooi uit, maar eerlijk gezegd begrijp ik niet wat elk stapje doet... kwestie van gebrek aan ervaring en alleen maar leren volgens het principe "Ik wil dat en dat bereiken en ik ga uitzoeken hoe dat moet". ;)
Niet de meest ideale manier natuurlijk, ik wil dan ook een keertje een (zelf?)cursus gaan volgen.

In elk geval ben ik blij met de functie zoals ik die nu heb (en die werkt), waarvoor dank. Ik begrijp trouwens wel dat het verstandig is om htmlspecialchars op te nemen?

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Ja, omdat je anders dat title attribuut afkapt als in een bepaald begrip quotes staan. :)

{signature}


Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Ik stuitte hier en daar op problemen waar er in de uitleg een ander begrip werd genoemd (A modifier en limiter hielpen niet).
Kwam er niet uit, dus ben nog eens naar Crisp's code gaan kijken. Daarbij kom ik erachter dat er in de output niets in het title-attribuut komt te staan. Output wordt nu bijvoorbeeld:
HTML:
1
2
<dfn title="">hamon</dfn><br />
<dfn title="">ha</dfn>


In dit stuk zou dus ergens de fout moeten zitten:
PHP:
1
\' . htmlspecialchars(\$begrip[strtolower(\'$0\')]) . \'

Maar ik kom er niet uit. Ben ik nou zo blind, of zit ik gewoon verkeerd te kijken? :?

Tjolk is lekker. overal en altijd.


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Dan bekijk je de waarde van $0, en zoek je daarna uit of die key (in lowercase) in $begrip voorkomt, etc. etc. Leer debuggen. :)

{signature}


Acties:
  • 0 Henk 'm!

  • Tjolk
  • Registratie: Juni 2007
  • Laatst online: 20:16
Hebbes! Ik zat verkeerd te kijken (krijg je als je al uren met dit soort dingen bezig bent). Met wat hulp van een frisse blik kwamen we erachter dat de fout zat in het stukje
code:
1
$begrip[strtolower(\'$0\')]

Daar was nog een ucfirst voor nodig. :)

PS @Voutloos: ik ben inderdaad nog aan het leren debuggen.

Tjolk is lekker. overal en altijd.

Pagina: 1