Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

How to use Substr to bulk substitutions?

by phoenix007 (Sexton)
on May 16, 2019 at 17:43 UTC ( [id://11100114]=perlquestion: print w/replies, xml ) Need Help??

phoenix007 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks
I am working on one string and I want to replace multiple substrings. I have offset and lengths already calculated. Now the problem I am facing with substr is positions I have currently will get changed as soon as I use substr once

#!/usr/bin/perl my $string = 'This is string'; my $capture1 = substr $string, 6, 2, "is stretched"; # This will subst +itute as expected my $capture2 = substr $string, 9, 6, 'STRING'; # Now here original str +ing will be updated and positions I have already are of no use.

One solution I can think of is add or subtract length difference in next offsets according to previous substr usages

Is there any other module or way to solve this problem ?

Replies are listed 'Best First'.
Re: How to use Substr to bulk substitutions?
by poj (Abbot) on May 16, 2019 at 18:05 UTC

    Can you reverse the order ?

    my $capture2 = $string, 9, 6, 'STRING'; my $capture1 = $string, 6, 2, "is stretched";
    poj

      It seems to be last option

Re: How to use Substr to bulk substitutions?
by Marshall (Canon) on May 17, 2019 at 03:00 UTC
    In general, using Perl regex is much better than running substr() multiple times on a single expression.
    Where are you using substr()?
    My version of Perl does not allow:
    my $capture1 = $string, 6, 2, "is stretched";
    #!/usr/bin/perl use strict; use warnings; my $string = 'This is string'; my $capture1 = $string, 6, 2, "is stretched"; # This will substitute a +s expected my $capture2 = $string, 9, 6, 'STRING'; # Now here original string wil +l be updated and positions I have already are of no use. __END__ Useless use of a constant (6) in void context at C:\test.pl line 6. Useless use of a constant (2) in void context at C:\test.pl line 6. Useless use of a constant ("is stretched") in void context at C:\test. +pl line 6. Useless use of a constant (9) in void context at C:\test.pl line 7. Useless use of a constant (6) in void context at C:test.pl line 7. Useless use of a constant ("STRING") in void context at C:\test.pl lin +e 7. Process completed successfully

      That was typo now I have corrected it

        Updated code:
        #!/usr/bin/perl use strict; use warnings; my $string = 'This is string'; my $capture1 = substr $string, 6, 2, "is stretched"; # This will subst +itute as expected # REALLY? print $string,"\n"; # I get: # "This iis stretchedstring" # perhaps better is: my $string2 = 'This is string'; $string2 =~ s/string/streched string/; print "$string2\n"; # that prints: # "This is streched string" # Your second substr(): substr $string, 9, 6, 'STRING'; print $string,"\n"; # that prints: # This iis STRINGhedstring # I suspect that you want: $string2 =~ s/string/STRING/; print "$string2\n"; # that prints: # "This is streched STRING"
        Perl is wonderful with what are called "regular expressions" or regex'es. In general, use the power of the Perl language. substr() is a low level thing that can be used, but is very, very rare in Perl programs. substr() is very common in C programs because there is not a "built in regex" operator.

        To summarize:

        #!/usr/bin/perl use strict; use warnings; my $string = "This is string"; print "$string\n"; $string =~ s/string/stretched string/; $string =~ s/string/STRING/; print "$string\n"; __END__ This is string This is stretched STRING
Re: How to use Substr to bulk substitutions?
by johngg (Canon) on May 16, 2019 at 18:46 UTC

    As poj suggests, using reverse allows you to use the positions without any messy arithmetic. Here is an example from another thread. A similar approach can be useful when splice'ing several elements into different positions in an array.

    Cheers,

    JohnGG

Re: How to use Substr to bulk substitutions? (updated)
by AnomalousMonk (Archbishop) on May 16, 2019 at 21:16 UTC

    Another example:

    c:\@Work\Perl\monks>perl -wMstrict -le "use Data::Dump qw(dd); ;; my $s = 'abXXcXXXXdefXXXXgXXhiXXXXXXXjkXX'; print qq{$s}; ;; my @off_len; while ($s =~ m{ X+ }xmsg) { push @off_len, [ $-[0], $+[0]-$-[0] ]; } dd \@off_len; ;; for my $ar_ol (reverse @off_len) { my ($off, $len) = @$ar_ol; substr $s, $off, $len, 'YYYYY'; } print qq{$s}; " abXXcXXXXdefXXXXgXXhiXXXXXXXjkXX [[2, 2], [5, 4], [12, 4], [17, 2], [21, 7], [30, 2]] abYYYYYcYYYYYdefYYYYYgYYYYYhiYYYYYjkYYYYY

    Update 1: Deleted a useless  my $len_s = length $s; statement left over from a previous iteration of the example code. Oops...

    Update 2: Here's another example that uses an arbitrary replacement array:

    c:\@Work\Perl\monks>perl -wMstrict -le "use Data::Dump qw(dd); ;; my $s = 'ab THE c RAIN def IN g SPAIN hi FALLS jk MAINLY l ON m'; print qq{$s}; ;; my @off_len; while ($s =~ m{ [[:upper:]]+ }xmsg) { push @off_len, [ $-[0], $+[0]-$-[0] ]; } dd \@off_len; ;; my @repl = qw(FOURSCORE AND 7 YEARS AGO OUR FATHERS XXX YYY); ;; my $least_i = $#off_len < $#repl ? $#off_len : $#repl; for my $rli (reverse 0 .. $least_i) { my ($off, $len) = @{ $off_len[$rli] }; substr $s, $off, $len, $repl[$rli]; } print qq{$s}; " ab THE c RAIN def IN g SPAIN hi FALLS jk MAINLY l ON m [[3, 3], [9, 4], [18, 2], [23, 5], [32, 5], [41, 6], [50, 2]] ab FOURSCORE c AND def 7 g YEARS hi AGO jk OUR l FATHERS m
    Try removing YYY, then XXX, then FATHERS from the  @repl array; try making this array empty. (Of course, it might be possible to integrate each offset/length array element pair with its replacement string in a single array, but I don't know the source of your data.)


    Give a man a fish:  <%-{-{-{-<

Re: How to use Substr to bulk substitutions?
by holli (Abbot) on May 16, 2019 at 21:08 UTC

      Yes it is related to that. I am trying to find out perticular tags in html for example a tags to skip them in my processing of making all url clickable. Simple I am trying to replace all urls with a href so that they become clickable. For that I am trying to skip a tags and all others with url will have a href inserted

        Simple I am trying to replace all urls with a href
        You mean, urls in text nodes? Someting like this?
        use Modern::Perl; use HTML::Parser; use Regexp::Common qw( URI ); my $parser = HTML::Parser->new ( default_h => [sub { my $something = shift; print $something }, 'text'], text_h => [sub { my $text = shift; # Guess a bit and add a scheme to www # This might be bit too aggressive $text =~ s^ www\.^ http://www.^gi; # Replace URLs in the text by links $text =~ s^($RE{URI})^<a href="$1">$1</a>^g; print $text; }, 'text'], ); $parser->parse( join "", <DATA>) || die $!; __DATA__ <!DOCTYPE html> <html> <head><title>Test data </title></head> <body> Some URL without scheme www.perlmonks.org and a plain URL with scheme http://perl.org and even ftp ftp://ftpserver.foo.com and an existing <a href="https://metacpan.org/">link</a> </body> </html>
        Output:
        <!DOCTYPE html> <html> <head><title>Test data </title></head> <body> Some URL without scheme <a href="www.perlmonks.org">www.perlmo +nks.org</a> and a plain URL with scheme <a href="http://perl.org">http://p +erl.org</a> and even ftp <a href="ftp://ftpserver.foo.com">ftp://ftpserver +.foo.com</a> and an existing <a href="https://metacpan.org/">link</a> </body> </html>


        holli

        You can lead your users to water, but alas, you cannot drown them.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11100114]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2024-04-23 06:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found