Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re: Regexp substitution using variables

by choroba (Archbishop)
on Nov 25, 2020 at 19:56 UTC ( #11124222=note: print w/replies, xml ) Need Help??


in reply to Regexp substitution using variables

Some of the flags can be moved to a non-capturing group:
#!/usr/bin/perl use warnings; use strict; my $string = 'abc'; my $pattern = 'B'; my $replacement = 'X'; my $flags = 'i'; $string =~ s/(?$flags:$pattern)/$replacement/; print $string; # aXc

But you can't do that for /gore.

Update: Even string eval doesn't help, as plain interpolation of the $replacement can break if it contains a slash.

eval "s/\$pattern/\$replacement/$flags"
doesn't work either, as you can't put $1 into $replacement unless you always use /ee which makes it unsafe again.

map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

Replies are listed 'Best First'.
Re^2: Regexp substitution using variables
by MikeTaylor (Acolyte) on Nov 25, 2020 at 22:24 UTC
    Thank you to all of you who have suggested the form s/(?$flags:$pattern)/$replacement/. I think this will get me much of what I need. The fact that the global-replace flag "g" doesn't work in this position is an annoying wrinkle, but I am going to take a deep breath and code up the with-g and without-g cases separately, depending on whether or not $flags =~ s/g// succeeds.
      > with-g and without-g cases separately, depending on whether or not $flags =~ s/g// succeeds.

      That's reasonable, because /g is not a simple modifier changing the match-rules, it turns the "replace" into a different "replace_all" command with very different behavior.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      but I am going to take a deep breath and code up the with-g and without-g cases separately
      What is the obsession of people to try and solve a complex problem like this in a single line of code, just because it can be done in one line of code in a perl script? I don't mean you specifically, but in general, like apparently most people who replied to this thread.

      Splitting this up in two parts makes sense, using /g is not a modifier of the pattern (as it is in several other languages), but of the substitution. Something like this looks acceptable to me as the (obvious) redundancy is actually quite limited:

      if($flages =~ s/g//) { s/(?:$flags)$pattern/replacement($replacement)/ge; } else { s/(?:$flags)$pattern/replacement($replacement)/e; }
      where you still have to provide the sub replacement.

      Other flags cannot really coded this way, but there's no need to provide for /o or /r at all, and allowing people to use /e flag in a config file, simply looks dangerous to me. If people would really want to use /e, it likely would be for just a handful of specific cases, and you can instead code a simpler solution for those cases (for the user, not necessarily for you) explicitly in your script, than having them write convoluted perl code.

      That real danger of allowing ordinary users to run arbitrary code, is also why I really don't like use of eval. It also enforces taking special care to be taken when writing the sub replacement. You can mitigate the danger by using a module like String::Interpolate, to embed captured values while disallowing access to the rest of the intestines of the script. .

      Well, this gets me much of what I need ... but I can't get back-references, either with $1 or \1. In either case, they appear as literals. Any ideas, other than eval?
        > but I can't get back-references,

        This works for me

        DB<44> $pat='(x)\1' DB<45> $mod='i' DB<46> 'xX' =~ /(?$mod)$pat/; say "$&:$1" xX:x DB<47>

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

      depending on whether or not $flags =~ s/g// succeeds

      Testing $flags =~ /g/; is simpler.

        Simpler, but doesn't quite do what I need. I want to supply the remaining flags, once g has been removed, in the (?$flags:...) part of the expression. And I need to remove the g so that I don't get flooded with noisy console warnings about how it's being ignored.
Re^2: Regexp substitution using variables
by Bod (Friar) on Nov 25, 2020 at 22:03 UTC

    Even string eval doesn't help, as plain interpolation of the $replacement can break if it contains a slash

    My first thought was to use eval but I hit a brick wall when I tried...
    I was thinking of first changing a slash in $replacement for a double slash then using eval to do the substitution but I got stuck getting the result of the substitution.

    $replacement =~ s/\\/\\\\/g; $string =~ eval "s/\$pattern/\$replacement/$flags";
    But that doesn't do it...

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://11124222]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2021-01-20 23:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?