[PHP] Overlappende strings replacen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hallo

Ik heb sinds kort een DVD burner en gebruik deze om back-ups te maken van bestanden. Ik heb meteen een PHP script geschreven waarmee ik door de backups kan zoeken en browsen. Werkt goed, de search functie doet alle bestandsnamen die aan de zoekcriteria voldoen bold maken (<B> en </B>). Maar nu wil ik dat niet de hele bestandsnaam bold word gemaakt, maar alleen de tekens in de bestandsnaam die aan de zoekcriteria voldoen. Voorbeeld: als ik als zoekcriteria FOO en DOC opgeef, moet hij <B>FOO</B>TBALL.<B>DOC</B> laten zien. Dit is het probleem niet, ik heb alle zoekcriteria in een array staan ($worldlist), en dan kan ik door dmv de volgende code alles replacen:

PHP:
1
2
3
4
5
6
    $string = "FOOTBALL.DOC";
    foreach ($wordlist as $word)
    {
        $string = str_replace($word, "<B>$word</B>", $string);
    }
    echo $string;


Je zou denken dat dit goed werkt. Maar er is een probleem: als ik zoek naar FOO en OOT, dan werkt het niet, omdat FOO en OOT allebei in $string voorkomen, maar elkaar overlappen. Als ik bovenstaande code uitvoer op met als zoekcriteria FOO en OOT, dan komt hij met: <B>FOO</B>TBALL.DOC, wat natuurlijk logisch is, omdat hij OOT niet meer kan vinden omdat er een </B> tag tussen staat. Maar wat ik wil is dat als hij ziet dat de twee strings elkaar overlappen, nog niet een </B> tag zet maar daarmee wacht todat er geen strings elkaar meer overlappen, dus in dit geval: <B>FOOT</B>BALL.DOC. Dit moet uiteraard ook werken met meerdere overlappende zoekcriteria bijv FOO OOT OTB BALL (in dit geval zou hij het hele woord FOOTBALL bold moeten maken door er <B> voor en </B> achter te zetten..).

Ik heb al vanalles geprobeerd maar ik kom er niet uit. Ik kon niets vinden over overlappende replacements in GoT search, groups.google, google,... :/. Ik vermoed dat het mogelijk is om te maken wat ik wil met 1 preg_replace regeltje, omdat ik weet dat regular expressions zeer krachtig kunnen zijn. Maar ik weet niets van regular expressions af, ik heb wel gezocht naar tutorials hierover, en ook allemaal dingen hieruit overgenomen en uitgeprobeerd, maar mijn probleem is vrij specifiek en door alleen tutorials te lezen kan het nog dagen/weken duren voordat ik genoeg van regular expressions af weet om mijn probleem op te lossen.

Ik vraag niet voor een kant en klaar script (zou natuurlijk ook welkom zijn :)), een duw in de goede richting zou volstaan.

Bedankt!

Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Dit is een lastige...

In principe is het niet fout als ie de overlappingen apart bolt maakt, maar mooi is anders natuurlijk. Wat je kan doen is niet de string-replace direct doen, maar de start- en eindpunten van de te highlighten delen opzoeken.

Vervolgens die ranges samenvoegen. Wat overigens geen pretje is om te doen (er kan overlap zijn doordat het startpunt van x kleiner en het eindpunt van x groter is dan het startpunt van y of als het startpunt van x kleiner is dan het eindpunt van y, maar groter dan het startpunt en het eindpunt van y weer kleiner dan het eindpunt van x, of als x in zijn geheel binnen y ligt). En dat dus voor alle combinaties van x en y, steeds een weghalend en de ranges samenvoegend.

[ Voor 9% gewijzigd door ACM op 25-07-2003 20:19 ]


Acties:
  • 0 Henk 'm!

  • bigtree
  • Registratie: Oktober 2000
  • Laatst online: 16-08 17:16
Andere oplossing; voor elk karakter een bitje bijhouden of hij gehighlight moet worden. Vervolgens de bitjes langslopen en start- en eindtags zetten waar nodig.

[ Voor 1% gewijzigd door bigtree op 25-07-2003 20:51 . Reden: typo ]

Lekker woordenboek, als je niet eens weet dat vandalen met een 'n' is.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Eerst heb ik ACM's methode geprobeerd, maar deze werd zo ingewikkeld dat ik daarmee gestopt ben.

Daarna bigtree's methode. En het werkt :*). 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
<?
    $wordlist[0] = "OOT";
    $wordlist[1] = "FOO";
    $string = "FOOTBALL.DOC"; 

    foreach ( $wordlist as $word )
    {
        $i = strpos($string, $word);
        if ( $i !== FALSE )
        {
            $len = strlen($word);
            for ($n = 0; $n < $len; $n++)
                $moetbold[$i+$n] = 1;
        }
    }

    $len = strlen($string);
    for ($i = 0; $i < $len; $i++)
    {
        if ( $moetbold[$i] )
            $output .= "<B>".$string[$i]."</B>";
        else
            $output .= $string[$i];
    }
    echo $output;
?>


Het is wel niet zo elegant als ik het wou hebben (ieder character wat bold moet zijn word tussen aparte <B> en </B> tags gezet), en waarschijnlijk ook niet zo snel, maar het werkt tenminste :).

ACM en bigtree: bedankt.

P.S. Ik sta nog steeds open voor een elegantere/snellere methode :)

Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Nou doe je alle karakters dus los?
Dat hoeft niet perse hoor :)

Je hoeft alleen bij de overgangen een b of /b te plaatsen, als er eerst een 0 was en dan ineens een 1 komt -> b en andersom een /b.

Lijkt me een kleine if om toe te voegen :)

Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

je kan natuurlijk ook nog string replace doen en alle "</b><b>"'s eruit filteren :P

euh en voordat iemand me voor dom aanziet, het is een grapje ;)

[ Voor 39% gewijzigd door Wolfboy op 26-07-2003 00:09 ]

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

Verwijderd

PHP:
1
2
3
4
5
6
7
8
if ($moetbold[$i] AND $preBold != TRUE) {
    $startPositie = $i;
    $preBold = TRUE;
} elseif ($preBold == TRUE) {
    $output = substr($string, 0, $startPositie-1) . "<B>" . substr($string, $startPositie + $x, $i - $startPositie) . "</B>" . substr($string, $i + $x);
    $x += 7;
    $preBold = FALSE;
}

Als je je if stukje in je for lus door deze vervangt denk ik dat het per gevonden woord werkt. Niet getest dus misschien is klein beetje debuggen nodig.
$preBold triggered zeg maar de kennis dat men nu in een reeks $moetbold[$i] = 1 zit en als dit afgelopen is wordt het gevonden woord gebold. Als er nog een keer een woord voorkomt dan worden bij de stringposities $x opgeteld omdat de <b> en </b> weer voor extra 'stringlengte' zorgen.

Deze code kan mooier, maar is zo ff los uit het handje zeg maar :) succes!

[ Voor 5% gewijzigd door Verwijderd op 26-07-2003 00:13 ]


Acties:
  • 0 Henk 'm!

Verwijderd

ACM schreef op 26 July 2003 @ 00:04:
Nou doe je alle karakters dus los?
Dat hoeft niet perse hoor :)

Je hoeft alleen bij de overgangen een b of /b te plaatsen, als er eerst een 0 was en dan ineens een 1 komt -> b en andersom een /b.

Lijkt me een kleine if om toe te voegen :)
Nu is mijn oplossing ineens weer veel te ingewikkeld geworden :).
Guru!

Acties:
  • 0 Henk 'm!

Verwijderd

Heb hier een tijdje geleden behoorlijk lang naar moeten zoeken, maar het werkt perfect:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function highlight($text, $searchwords) {

    $text = "<x>" . $text . "<x>";

    foreach ($searchwords as $word) {
        if (trim($word)){
            $text = preg_replace("/(\>(((?>[^><]+)|(?R))*)\<)/ie",
                "preg_replace('/\b(?>($word)+)\b/i', 
                '<span class=\"highlight\">\\\\1</span>', '\\1')", $text);
            $text = stripslashes($text);
        }
    }
    return $text;
}


In gaat de text waarin je zoek ($text) én de array met woorden ($searchwords). Je maakt in je html pagina een css class aan (.highlight in dit geval) en de instellingen daarin worden toegepast op de zoekwoorden:

code:
1
2
3
4
5
6
<style>
    .highlight { 
        color: red;
        font-weight: bold;
    } 
</style>


Maakt alle woorden rood en vetgedrukt.. :)

(btw de <x> tags die toegevoegd worden zijn een wat mindere hack, maar nodig omdat er op tekens buiten html tags gezocht wordt. dit om bestaande html niet te wijzigen)

[ Voor 43% gewijzigd door Verwijderd op 26-07-2003 00:28 ]


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Code komt me bekend voor...

Maar lost niet het probleem van de topicstarter hier op, bij mijn weten.

Wat ik trouwens bedoelde is zoiets:
PHP:
1
2
3
4
5
6
7
8
9
10
11
$output = '';
$wasbold = 0;
for($i = 0; $i < count($moetbold); $i++)
{
  if($wasbold != $moetbold[$i])
  {
     $output .= ($wasbold == 0 ? '<b>' : '</b>' );
     $wasbold = $moetbold[$i];
  }
  $output .= $string{$i};
}

Acties:
  • 0 Henk 'm!

  • bigtree
  • Registratie: Oktober 2000
  • Laatst online: 16-08 17:16
ACM schreef op 26 juli 2003 @ 11:36:
Code komt me bekend voor...

Maar lost niet het probleem van de topicstarter hier op, bij mijn weten.

Wat ik trouwens bedoelde is zoiets:
PHP:
1
2
3
4
5
6
7
8
9
10
11
$output = '';
$wasbold = 0;
for($i = 0; $i < count($moetbold); $i++)
{
  if($wasbold != $moetbold[$i])
  {
     $output .= ($wasbold == 0 ? '<b>' : '</b>' );
     $wasbold = $moetbold[$i];
  }
  $output .= $string{$i};
}
En als het laatste teken nou bold is?
Even toevoegen na de for-loop:
PHP:
1
$output .= ($wasbold == 1 ? '</b>' : '');

Lekker woordenboek, als je niet eens weet dat vandalen met een 'n' is.


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

bigtree schreef op 26 July 2003 @ 12:32:
En als het laatste teken nou bold is?
ACM schreef op 26 July 2003 @ 11:36:
Wat ik trouwens bedoelde is zoiets:
Ik mag toch hopen dat daaruit blijkt dat de code nog niet compleet is/was :P

Acties:
  • 0 Henk 'm!

  • bigtree
  • Registratie: Oktober 2000
  • Laatst online: 16-08 17:16
ACM schreef op 26 July 2003 @ 12:45:
[...]


[...]

Ik mag toch hopen dat daaruit blijkt dat de code nog niet compleet is/was :P
Smoesjes, smoesjes. :+
Mij maak je niet wijs dat je die code uit het hoofd hebt gepost zonder te testen. :P

Lekker woordenboek, als je niet eens weet dat vandalen met een 'n' is.


Acties:
  • 0 Henk 'm!

  • Spider.007
  • Registratie: December 2000
  • Niet online

Spider.007

* Tetragrammaton

Ik ben ook bezig geweest met een dergelijk script; en dat werd altijd leuk als de gebruiker zocht op 'b'; die werd namelijk vervangen door <b>b</b> waarna dit werd vervangen door
HTML:
1
<<b>b</b>><b>b</b></<b>b</b>>
:X

uiteindelijk heb ik het zo opgelost:

PHP:
1
2
3
4
5
6
7
    foreach($_SESSION["keywordsToHightlight"] as $keywordShouldBeHightlight){
        if (trim($keywordShouldBeHightlight)!=""){
            for ($i=0; $i<10; $i++){
                $output = preg_replace('/>([^<]*)('.preg_quote($keywordShouldBeHightlight, "/").')([^<]*)</i', '>$1<span class="sH2">$2</span>$3<', $output);
                $output= preg_replace('/&([^&<]*)<([^>]*)>([^;]*);/i', '&$1$3;', $output);
            }
        }
:)

---
Prozium - The great nepenthe. Opiate of our masses. Glue of our great society. Salve and salvation, it has delivered us from pathos, from sorrow, the deepest chasms of melancholy and hate


Acties:
  • 0 Henk 'm!

  • Rotjeknor
  • Registratie: April 2001
  • Laatst online: 01-04-2023
Tis maar een idee, maar je kan ook nog een oplossing verzinnen via de andere kant. Tot nu toe werd er geredeneerd dat je met de output aan de gang moest, maar je kan het natuurlijk ook aan de input veranderen. Als je bv FOO en OOT hebt om te zoeken, kan je dat gaan combineren tot FOOT, FOOOT, FOOOOT en OOTFOO.

Dit is alleen maar een idee, bedoeld om het licht ook op een andere kant te laten schijnen. Ik weet niet precies of dit wel of niet eleganter is dan de vorige oplossingen, die er ook wel goed uitzien. Misschien kan je er iets mee, misschien niet, kijk maar (-:

Ook Knor is aangestoken met het ligfietsvirus!


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
De code werkt nu precies zoals ik het wil. Ik heb het nu zo gemaakt dat hij niet om ieder karakter een <B> en </B> tag zet, naar aanleiding van ACM's idee.
De preg_replace oplossingen van mmmuttley en Spider.007 werken niet goed bij mij (???).

Ik heb verder nog wat dingen toegevoegd: case insensivite search en htmlspecialchars om de HTML code te escapen.
Ik heb (nog) geen bugs gevonden, dus het zou goed moeten werken..
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?
    // Dit voorbeeld laat het replacen van overlappende strings, automatische html escaping en
    // variable start- en eind tags zien:
    $wordlist[0] = "200";
    $wordlist[1] = "003";
    $string = "<U>2003</U>";
    echo highlight($string, $wordlist, "<font color=\"#FF0000\"><b>", "</b></font>");
    // Output: &lt;U&gt;<font color="#FF0000"><b>2003</b></font>&lt;/U&gt;

    function highlight($string, $wordlist, $begintag, $eindtag)
    {
        if ( !$wordlist )
            return htmlspecialchars($string);
        foreach ( $wordlist as $word )
        {
            $i = 0;

            // strtolower() voor een case insensitive search
            while ( ($i = strpos(strtolower($string), strtolower($word), $i)) !== FALSE )
            {
                $len = strlen($word);
                for ($n = 0; $n < $len; $n++)
                    $moetbold[$i+$n] = 1;
                $i++;
            }
        }

        $len = strlen($string);
        $boldactief = FALSE;
        $output = "";
        for ($i = 0; $i < $len; $i++) 
        {
            if ( $moetbold[$i] && !$boldactief )
            {
                $output .= $begintag;
                $boldactief = TRUE;
            }
            
            // htmlspecialchars() zodat de user geen HTML kan inserten
            $output .= htmlspecialchars($string[$i]);
            
            if ( !$moetbold[$i+1] && $boldactief )
            {
                $output .= $eindtag; 
                $boldactief = FALSE;
            }
        }
        return $output;
    }
?>

Rotjeknor: Als de gebruiker (meestal ikzelf :)) 15 ofzo zoekwoorden opgeeft dan moet je die allemaal gaan combineren etc en dat lijkt me nogal veel tijd in beslag nemen.

Ok, het werkt nu dus goed. Ik heb van de meeste reacties wel een ideetje kunnen gebruiken, hartelijk bedankt allemaal :).

Acties:
  • 0 Henk 'm!

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Spider.007 schreef::
Ik ben ook bezig geweest met een dergelijk script; en dat werd altijd leuk als de gebruiker zocht op 'b'; die werd namelijk vervangen door b waarna dit werd vervangen door [..]
:D, altijd geniaal dat soort dingen. Dat je ook midden in de compuzaal zit te lachen om je eigen suffe fout en als iemand vraagt 'wat is er?', dat je dan denkt: nah, dit ga ik maar niet uitleggen. Typisch zo'n grap die niet leuk is als je hem uitlegt :)

Wie trösten wir uns, die Mörder aller Mörder?


Acties:
  • 0 Henk 'm!

  • Spider.007
  • Registratie: December 2000
  • Niet online

Spider.007

* Tetragrammaton

Fused schreef op 26 July 2003 @ 21:24:
[...]

:D, altijd geniaal dat soort dingen. Dat je ook midden in de compuzaal zit te lachen om je eigen suffe fout en als iemand vraagt 'wat is er?', dat je dan denkt: nah, dit ga ik maar niet uitleggen. Typisch zo'n grap die niet leuk is als je hem uitlegt :)
Joh, ik zat te testen met een aantal collega's terwijl we ons afvroegen waarom alles zo traag ging en het geheugengebruik zo omhoog schoot.... Toen ik view source deed ging Mozilla even 200 Mb aan geheugen trekken :D Toen maar ff in IE (Die doet geen nieuwe request bij view-source) en bleken er megabytes aan <b><b><b> tags te worden doorgestuurd 8)

---
Prozium - The great nepenthe. Opiate of our masses. Glue of our great society. Salve and salvation, it has delivered us from pathos, from sorrow, the deepest chasms of melancholy and hate

Pagina: 1