I'm making a regexp constructor.
But when running:
my $text = 'a a a';
my $replace = '$1/$2-$3';
$text =~ s/(\w) (\w+) (\w+)/$replace/gmi;
$text here = '$1/$2-$3';
So $1,$2,$3 are not changed but placed as they are in $replace. How would i make it use $replace content as manually printed replate pattern?
$replace is just a string. If you want it to be evaluated as code, you need the /e modifier in your substitution. But you also need to prepare your string for the evaluation to interpolate your variables:
my $replace = 'qq($1/$2-$3)';
$text =~ s/(\w) (\w+) (\w+)/$replace/gmiee;
We use double evaluation to first turn the variable into a string, then to evaluate that string.
However, whenever you find yourself relying on eval, you're probably doing something unnecessary. Eval can be rather evil, as OmnipotentEntity rightly points out, so be very careful about using it.
Related
Given :
my $str = "foo95285734776bar";
$str =~ s/([0-9]{2,4})/_????_/g;
What single regex where '????' is the length of $1 can produce output "foo_4__4__3_bar" ?
That is, where "9528" is replaced with "_4_", "5734" with "_4_", and the remaining "776" with "_3_".
You can use the /e modifier to add Perl code into the substitution part that is then evaled.
my $str = "foo95285734776bar";
$str =~ s/([0-9]{2,4})/'_' . length($1) . '_'/ge;
print $str;
Will output
foo_4__4__3_bar
Note that you now need a full Perl expression there. That's why you have to actually quote and concatenate the underscores.
From perlop:
A /e will cause the replacement portion to be treated as a full-fledged Perl expression and evaluated right then and there. It is, however, syntax checked at compile-time. A second e modifier will cause the replacement portion to be evaled before being run as a Perl expression.
My simple script in Perl
#!/usr/bin/perl
use v5.20.1;
use strict;
use warnings;
$| = 1;
my $string = $ARGV[ 0 ];
my $match = $ARGV[ 1 ];
my $substitute = $ARGV[ 2 ];
$string =~ m/match/g; # just for capturing the pattern
$string =~ s/$match/only $substitute/g;
say "\$1[itself] == $1";
say "\$substitute == $substitute";
say "\$string == $string";
say "prefix == $`";
say "match == $&";
say "suffix == $'";
Input and Output:
$ perl temp 'I have 9 dollars' '(\d+)' '$1'
$1[itself] == 9
$substitute == $1
$string == I have only $1 dollars
prefix == I have
match == 9
suffix == dollars
A thing that I am trying to do is a simple substitution by initializing the match and substitution variable from #ARGV. After that a simple match for initializing the special character such as $1. But when I want to pass the special character $1 from command-line to the script, it is deducted as a regular string and not the special variable $1 that I need for substitution
If I change the code from:
$string =~ s/$match/only $substitute/g;
to:
$string =~ s/$match/only $1/g;
now it works!
1. Why $1 of the command line is different from $1 in the script?
2. Is there any way to solve it?
A screenshot of my console:
Edit
As ikegami suggested I installed and used the String::Substitution and could by using gsub_copy($line, $match, $substitute); deduced the substitution.
But still I need to use this substitution itself for printing it on the screen and colorize it for illustration that what happens.
In fact this purpose, is part of a rename script that read all file and by call rename function, changes the name of the files.
As you can see on the screenshot:
instead of for_$1 it should be for_Level on the screen. So does this module can return what it has deduced?
I am not talking about the sub match of the match group parentheses!
Also the author of the module has said:
This module does not save or interpolate $& to avoid the "considerable
performance penalty"
May this screenshot clarify the subject:
Why $1 of the command line is different from $1 in the script?
You're actually asking why
s/.../...$substitute.../
in your script is different than
s/.../...$1.../
in your script. For the same reason that
print($substitute);
is different than
print($1);
and that is that the value of $1 (the captured text) is different than the value of $substitute ($1).
This is what you need:
use String::Substitution qw( gsub_modify );
gsub_modify($string, $match, $substitue);
That assumes you don't actually need $1 outside of the replacement expression, and that you don't actually need $& and friends at all. If you do need them, then you can use the following:
use String::Substitution qw( interpolate_match_vars last_match_vars );
$string =~ s/$match/ interpolate_match_vars($substitute, last_match_vars()) /eg;
Obligatory discouragement: don't do this. Basically this is just running eval on a user-supplied string, which is really a bad idea.
Actual answer: use the ee modifer (see perlop) to s/// to eval the string in the right hand part of the expression:
$string =~ s/$match/"\"only $substitute\""/gee
By the way, you don't need to capture the pattern separately, just put the $match in a capturing group in the s/// expression:
$string =~ s/($match)/"\"only $substitute\""/gee
Given the following code,
my $string = "foo";
my $regex = s/foo/bar/;
$string =~ $regex;
print $string, "\n";
I would have expected the output to be bar, however it is foo. Why is that the case, and how can I solve that problem?
Note that in my actual case, the regex is more complicated, and I actually want to store several of them in a hash (so I can write something like $string =~ $rules{$key}).
You're looking for substitution, not only the regex part so I guess compiled regex (qr//) is not what you're looking for,
use strict;
use warnings;
my $string = "foo";
my $regex = sub { $_[0] =~ s/foo/bar/ };
$regex->($string);
print $string, "\n";
Your statement
my $regex = s/foo/bar/
is equivalent to
my $regex = $_ =~ s/foo/bar/
s/// returns the number of substitutions made, or it returns false (specifically, the empty string). So $regex is now '' or 1 (it could be more if the /g modifier was in effect) and
$string =~ $regex
is doing 'foo' =~ // or 'foo' =~ /1/ depending on what $_ contained originally.
You can store a regex pattern in a variable but, in your example, the regex is just foo, and there is a lot more going on than just that pattern
The statement s/foo/bar/ is more complex than it seems -- it is a fully-fledged statement that applies a regex pattern to a target string and substitutes a replacement string if the pattern is found. In this case the target string is the default variable $_ and the replacement string is foo. You could think of it as a call to a subroutine
substitute($_, 'foo', 'bar')
and the regex pattern is only the second parameter
What you can do is store a regex pattern. The regex part of that substitution is foo, and you can say
my $pattern = qr/foo/;
s/$pattern/bar/;
But you really should explain the problem that you're trying to solve so that we can help you better
In the assignment, you need to tell Perl not to evaluate the regular expression but just to keep it. This is what qr is for.
But you can't do this with whole substitutions, which is why Сухой27 suggests using a subroutine.
I have a string $text and want to modify it with a regex. The string contains multiple sections like <NAME>John</NAME>.
I want to search for those sections, which I would normally do with something like
$text =~ m/<NAME>(.*?)<\/NAME>/g
but then make sure that there are no leading and trailing blanks and no leading non-word characters, which I would normally ensure with something like
$temp =~ s/^\s+|\s+$//g; # trim leading and trailing whitespaces
$temp = s/^\W*//g; # remove all leading non-word chars
Now my question is: How do I actually make this happen? Is it possible to use a s/// regex instead of the m//?
This is possible in a single substitution, but it's unnecessarily complex. I suggest you do a two-tier substitution using a executable replacement.
my $text = '<NAME> %^John^%
</NAME>';
$text =~ s{ (?<=<NAME>) ([^<>]*) (?=</NAME>) }{
(my $new = $1) =~ s/\A\s+|\s+\z//g;
$new =~ s/\A\W+//;
$new;
}eg;
print $text;
output
<NAME>John^%</NAME>
This is even simpler if you have version 14 or later of Perl 5, and want to use the non-destructive ( /r modifier) substitution mode.
$text =~ s{ (?<=<NAME>) ([^<>]*) (?=</NAME>) }{ $1 =~ s/\A\s+|\s+\z//gr =~ s/\A\W+//r }exg;
If I understand correctly, what you want to do is merely "clean up" the text inside the tag (insofar as it's possible to "parse" XML using regular expressions). This should do the trick:
$text =~ s/(<NAME>)\s*\W*(.*?)\s*(<\/NAME>)/$1$2$3/sgi;
I want to be able to do a regex match on a variable and assign the results to the variable itself. What is the best way to do it?
I want to essentially combine lines 2 and 3 in a single line of code:
$variable = "some string";
$variable =~ /(find something).*/;
$variable = $1;
Is there a shorter/simpler way to do this? Am I missing something?
my($variable) = "some string" =~ /(e\s*str)/;
This works because
If the /g option is not used, m// in list context returns a list consisting of the subexpressions matched by the parentheses in the pattern, i.e., ($1, $2, $3 …).
and because my($variable) = ... (note the parentheses around the scalar) supplies list context to the match.
If the pattern fails to match, $variable gets the undefined value.
Why do you want it to be shorter? Does is really matter?
$variable = $1 if $variable =~ /(find something).*/;
If you are worried about the variable name or doing this repeatedly, wrap the thing in a subroutine and forget about it:
some_sub( $variable, qr/pattern/ );
sub some_sub { $_[0] = $1 if eval { $_[0] =~ m/$_[1]/ }; $1 };
However you implement it, the point of the subroutine is to make it reuseable so you give a particular set of lines a short name that stands in their place.
Several other answers mention a destructive substitution:
( my $new = $variable ) =~ s/pattern/replacement/;
I tend to keep the original data around, and Perl v5.14 has an /r flag that leaves the original alone and returns a new string with the replacement (instead of the count of replacements):
my $match = $variable =~ s/pattern/replacement/r;
Well, you could say
my $variable;
($variable) = ($variable = "find something soon") =~ /(find something).*/;
or
(my $variable = "find something soon") =~ s/^.*?(find something).*/$1/;
You can do substitution as:
$a = 'stackoverflow';
$a =~ s/(\w+)overflow/$1/;
$a is now "stack"
From Perl Cookbook 2nd ed
6.1 Copying and Substituting Simultaneously
$dst = $src;
$dst =~ s/this/that/;
becomes
($dst = $src) =~ s/this/that/;
I just assumed everyone did it this way, amazed that no one gave this answer.
Almost ....
You can combine the match and retrieve the matched value with a substitution.
$variable =~ s/.*(find something).*/$1/;
AFAIK, You will always have to copy the value though, unless you do not care to clobber the original.
$variable2 = "stackoverflow";
(my $variable1) = ($variable2 =~ /stack(\w+)/);
$variable1 now equals "overflow".
I do this:
#!/usr/bin/perl
$target = "n: 123";
my ($target) = $target =~ /n:\s*(\d+)/g;
print $target; # the var $target now is "123"
Also, to amplify the accepted answer using the ternary operator to allow you to specify a default if there is no match:
my $match = $variable =~ /(*pattern*).*/ ? $1 : *defaultValue*;