2

I am trying to search for a sub string in a string and highlight the search text. What I have so far is this:

$text = 'Guitar';
$search = 'guit';

With the code below I return the string 'guitar' with 'guit' highlighted but I would like to return the original 'Guit' highlighted with the capital 'G'. Any help would be appreciated.

my $replace = "<span style='color:white;background- color:red'>$search</span>";
$text =~ s/$search/$replace/gi;
Avinash Raj
  • 172,303
  • 28
  • 230
  • 274
user3287677
  • 37
  • 2
  • 8

4 Answers4

0

Use $&, which will be set to the matched string:

my $text = "Albert Hammond's guitar from Gibraltar.";

foreach my $text ('guitar', 'Gibraltar') {
    my $replace = "<span style='color:white;background-color:red'>" . $& . "</span>";
    $text =~ s/$search/$replace/gi;
}

Update: this won't work, as @ThisSuitIsBlackNot comments below: the $& will not be interpreted in the replacement string when that string is a variable. (I didn't know this.) So you have to get rid of the variable:

my $text = "Albert Hammond's guitar from Gibraltar.";

foreach my $search ('guitar', 'Gibraltar') {
    my $replace = "";
    $text =~ s/$search/<span style='color:white;background-color:red>$&<\/span>/gi;
}

This prints

Albert Hammond's <span style='color:white;background-color:red>guitar</span> from <span style='color:white;background-color:red>Gibraltar</span>.

Note that I also had to escape the / in the replacement string because / is also used to delimit the arguments of the s command.

This may still not be good enough.

It assumes your values for $search will never contain any characters that are special to regular expressions (such as . or *). If they might, you can escape them using qr.

It also assumes those values never contain any characters special to XML (such as < or >, or the ' in my example.). If they might, use an XML manipulation library such as XML::LibXML to make the changes, or use XSLT.

reinierpost
  • 8,425
  • 1
  • 38
  • 70
  • `$&` is only set *after* a match, so that won't work (for [performance reasons](http://perldoc.perl.org/perlvar.html#Performance-issues), you generally shouldn't use `$&` on Perl <5.20 anyway; even then, I would use a capturing group instead). – ThisSuitIsBlackNot Aug 24 '15 at 19:31
  • It won't work because `$&` is `undef` when you concatenate it, so `$replace` is set to an empty span tag. You're looking for `s/foo/bar$&baz/` or `s/foo/"bar".$&."baz"/e` or `s/foo/"qq{$replace}"/ee`. – ThisSuitIsBlackNot Aug 25 '15 at 14:51
  • You're absolutely correct - I never knew this. Changed the answer accordingly. – reinierpost Aug 25 '15 at 15:12
  • 1
    Just a small tip - you can use any character as separator for `s`, just like in `sed`, so if you don't want to worry about escaping slashes, you can use for example `@`, like this `$text =~ s@$search@ – ashrasmun Jan 15 '22 at 09:26
0

This did the trick.

my ($original) = $text =~ m/$search/gi;
my $replace = "<span style='color:white;background-color:red'>$original</span>";
$text =~ s/$search/$replace/gi;
user3287677
  • 37
  • 2
  • 8
0

You have made this difficult for yourself by requiring the replacement string to be held in a scalar variable. Without that

$text =~ s{($search)}{<span style='color:white;background-color:red'>$1</span>}gi;

would work fine

As it is you need the /ee eval expression modifier, like this

use strict;
use warnings;
use 5.010;

my $text   = 'Guitar';
my $search = 'guit';

my $replace = q{<span style='color:white;background-color:red'>$1</span>};

$text =~ s/($search)/ "qq{$replace}" /eegi;

say $text

output

<span style='color:white;background-color:red'>Guit</span>ar
Borodin
  • 126,100
  • 9
  • 70
  • 144
0

Here a method with the string function. I made it in the context of Webmin, but suppose it will work too in other contexts.

sub html_escape { return $_[0] } ; 
# Not the actual function in Webmin...  
# The real one crashes if provided with undef.

sub highlight {
    my ($txt, $high) = @_;
    if (!$txt || !$high) {
        return $txt;
    };
    my $previous_index = 0;
    my $index = index lc($txt),lc($high), $previous_index;
    my @splitted = ();
    while(-1 < $index) {
        my $before  = substr $txt, $previous_index, $index-$previous_index;
        my $content = substr $txt, $index , length $high ;
        $previous_index =  $index + length $high;
        push @splitted, html_escape($before) if $before;
        push @splitted, '<b>'.html_escape($content).'</b>' if $content ;
        $index = index lc($txt),lc($high), $previous_index;
    }
    if (@splitted) {
        my $last = substr $txt, $previous_index ;
        push @splitted, html_escape($last) if $last;
        return join q{}, @splitted,;
    };
    return html_escape($txt);
}

Feel free to bring futher comment this.

Use as follows, use css to determine what is the highlight.

print highligh('abege', 'e');
# ab<b>e</b>g<b>e</b>
MUY Belgium
  • 2,330
  • 4
  • 30
  • 46