Regular expression chaining/mixing in perl - regex

Consider the following:
my $p1 = "(a|e|o)";
my $p2 = "(x|y|z)";
$text =~ s/($p1)( )[$p2]([0-9])/some action to be done/g;
Is the regular expression pattern in string form equal to the concatenation of the elements in the above? That is, can the above can be written as
$text =~ s/((a|e|o))( )[(x|y|z)]([0-9])/ some action to be done/g;

Well, yes, variables in a pattern get interpolated into the pattern, in a double-quoted context, and the expressions you show are equivalent. See discussion in perlretut tutorial.
(I suggest using qr operator for that instead. See discussion of that in perlretut as well.)
But that pattern clearly isn't right
Why those double parenthesis, ((a|e|o))? Either have the alternation in the variable and capture it in the regex
my $p1 = 'a|e|o'; # then use in a regex as
/($p1)/ # gets interpolated into: /(a|e|o)/
or indicate capture in the variable but then drop parens in the regex
my $p1 = '(a|e|o)'; # use as
/$p1/ # (same as above)
The capturing parenthesis do their job in either way and in both cases a match ( a or e or o) is captured into a suitable variable, $1 in your expression since this is the first capture
A pattern of [(x|y|z)] matches either one of the characters (, x, |,... (etc) -- that [...] is the character class, which matches either of the characters inside (a few have a special meaning). So, again, either use the alternation and capture in your variable
my $p2 = '(x|y|z)'; # then use as
/$p2/
or do it using the character class
my $p2 = 'xyz'; # and use as
/([$p2])/ # --> /([xyz])/
So altogether you'd have something like
use warnings;
use strict;
use feature 'say';
my $text = shift // q(e z7);
my $p1 = 'a|e|o';
my $p2 = 'xyz';
$text =~ s/($p1)(\s)([$p2])([0-9])/replacement/g;
say $_ // 'undef' for $1, $2, $3, $4;
I added \s instead of a literal single space, and I capture the character-class match with () (the pattern from the question doesn't), since that seems to be wanted.

Neither snippets are valid Perl code. They are therefore equivalent, but only in the sense that neither will compile.
But say you have a valid m//, s/// or qr// operator. Then yes, variables in the pattern would be handled as you describe.
For example,
my $p1 = "(a|e|o)";
my $p2 = "(x|y|z)";
$text =~ /($pl)( )[$p2]([0-9])/g;
is equivalent to
$text =~ /((a|e|o))( )[(x|y|z)]([0-9])/g;
As mentioned in an answer to a previous question of yours, (x|y|z) is surely a bug, and should be xyz.

Related

What is the difference between qr/ and m/ in Perl?

From Perldoc:
qr/STRING/msixpodualn
This operator quotes (and possibly compiles) its STRING as a regular
expression. STRING is interpolated the same way as PATTERN in
m/PATTERN/.
m/PATTERN/msixpodualngc
/PATTERN/msixpodualngc
Searches a string for a pattern match, and in scalar context returns
true if it succeeds, false if it fails. If no string is specified via
the =~ or !~ operator, the $_ string is searched. (The string
specified with =~ need not be an lvalue--it may be the result of an
expression evaluation, but remember the =~ binds rather tightly.) See
also perlre.
Options are as described in qr// above
I'm sure I'm missing something obvious, but it's not clear at all to me how these options are different - they seem basically synonymous. When would you use qr// instead of m//, or vice versa?
The m// operator is for matching, whereas qr// produces a pattern (as a string) that you can stick in a variable and store for later. It's a quoted regular expression pattern.
Pre-compiling the this way is useful for optimising your run-time cost, e.g. if you are using a fixed pattern in a loop with millions of iterations, or you want to pass patterns around between function calls or use them in a dispatch table.
# match now
if ( $foo =~ m/pattern/ ) { ... }
# compile and use later
my $pattern = qr/pattern/i;
print $pattern; # (?^ui:pattern)
if ($foo =~ m/$pattern/) { ... }
The structure of the string (in this example, (?^ui:pattern)) is explained in perlre. Basically (?:) creates a sub-pattern with built-in flags, and the ^ says which flags not to have. You can use this inside other patterns too, to turn on and off case-insensitivity for parts of your pattern for example.

Why does this regex not evaluate to true in perl?

I'm wrapping my first content string in single quotes to simplify what I'm asking. Its not wrapped in my code.
'{
# "ERROR" : [
# "generic error message"
# ]
# }' =~ {
It seems like this should be true because I'm just checking the first expression for a open bracket but its evaluating to false.
In languages like PHP, a regex is just an ordinary string, complete with quotes: "/regex/". Perl, on the other hand, has regex literals. These are directly embedded into the code, but still need delimiters.
In general, this looks like:
$string =~ /regex/
The slashes are the default delimiter. if slashes are an inconvenient delimiter, you can choose any other(1)—just prefix the regex with m (for match). These matches are equivalent:
$url =~ /http:\/\///;
$url =~ m/http:\/\///;
$url =~ m{http://};
If you want to store a precompiled regex in a variable, you can use qr// quotes. Prefer these to plain strings, because qr// regexes follow the same quoting rules as regexes (no double escapes):
my $url_regex = qr~http://~;
$url =~ $url_regex;
The =~ bind operator tries its best to interpret the right hand argument as a regex, so you could even use strings, in a php-like fashion:
"123\nab12" =~ "\\d\n\\w+"; # double escaping sucks, still matches
but this is inferior to /\d\n\w+/.
Therefore, is not possible to skip any delimiter whatsoever:
# "=~" has higher precedence than "+".
# Establishing precedence is the *sole* task of the parens here
"112" =~ (1+2); # fails
"3" =~ (1+2); # matches
"112" =~ /1+2/; # matches
Don't use question marks as regex delimiters, these match only once.
Try this:
'your string' =~ /\{/
Your syntax is not valid as shown because { is not a regular expression. To fix that issue, I wrapped it in the default regular expression characters to get /{/, and then escaped { (resulting in /\{/) because it's a metacharacter of regular expression syntax. (For the regex x{a,b} means that x should be repeated at least a and at most b times.)

Metaquoting patterns in a variable list

I have a list of patterns I want to look for in a string. These patterns are numerous and contain numerous metacharacters that I want to just match literally. So this is the perfect application for metaquoting with \Q..\E. The complication is that I need to join the variable list of patterns into a regular expression.
use strict;
use warnings;
# sample string to represent my problem
my $string = "{{a|!}} Abra\n{{b|!!}} {{b}} Hocus {{s|?}} Kedabra\n{{b|+?}} {{b|??}} Pocus\n {{s|?}}Alakazam\n";
# sample patterns to look for
my #patterns = qw({{a|!}} {{s|?}} {{s|+?}} {{b|?}});
# since these patterns can be anything, I join the resulting array into a variable-length regex
my $regex = join("|",#patterns);
my #matched = $string =~ /$regex(\s\w+\s)/; # Error in matching regex due to unquoted metacharacters
print join("", #matched); # intended result: Hocus\n Pocus\n
When I attempt to introduce metaquoting into the joining operation, they appear to have no effect.
# quote all patterns so that they match literally, but make sure the alternating metacharacter works as intended
my $qmregex = "\Q".join("\E|\Q", #patterns)."\E";
my #matched = $string =~ /$qmregex(\s\w+\s)/; # The same error
For some reason the metaquoting has no effect when it is included in the string I use as the regular expression. For me, they only work when they are added directly to a regex as in /\Q$anexpression\E/ but as far as I can tell this isn't an option for me. How do I get around this?
I don't understand your expected result, as Abra and Kedabra are the only strings preceded by any of the patterns.
To solve your problem you must escape each component of the regex separately as \Q and \E affect only the value of the string in which they appear, so "\Q" and "\E" are just the null string "" and "\E|\Q" is just "|". You could write
my $qmregex = join '|', map "\Q$_\E", #patterns;
but it is simpler to call the quotemeta function.
You must also enclose the list in parentheses (?:...) to isolate the alternation, and apply the /g modifier to the regex match to find all ocurrences within the string.
Try
use strict;
use warnings;
my $string = "{{a|!}} Abra\n{{b|!!}} {{b}} Hocus {{s|?}} Kedabra\n{{b|+?}} {{b|??}} Pocus\n {{s|?}}Alakazam\n";
my #patterns = qw( {{a|!}} {{s|?}} {{s|+?}} {{b|?}} );
my $regex = join '|', map quotemeta, #patterns;
my #matched = $string =~ /(?:$regex)(\s\w+\s)/g;
print #matched;
output
Abra
Kedabra

How to have a variable as regex in Perl

I think this question is repeated, but searching wasn't helpful for me.
my $pattern = "javascript:window.open\('([^']+)'\);";
$mech->content =~ m/($pattern)/;
print $1;
I want to have an external $pattern in the regular expression. How can I do this? The current one returns:
Use of uninitialized value $1 in print at main.pm line 20.
$1 was empty, so the match did not succeed. I'll make up a constant string in my example of which I know that it will match the pattern.
Declare your regular expression with qr, not as a simple string. Also, you're capturing twice, once in $pattern for the open call's parentheses, once in the m operator for the whole thing, therefore you get two results. Instead of $1, $2 etc. I prefer to assign the results to an array.
my $pattern = qr"javascript:window.open\('([^']+)'\);";
my $content = "javascript:window.open('something');";
my #results = $content =~ m/($pattern)/;
# expression return array
# (
# q{javascript:window.open('something');'},
# 'something'
# )
When I compile that string into a regex, like so:
my $pattern = "javascript:window.open\('([^']+)'\);";
my $regex = qr/$pattern/;
I get just what I think I should get, following regex:
(?-xism:javascript:window.open('([^']+)');)/
Notice that it it is looking for a capture group and not an open paren at the end of 'open'. And in that capture group, the first thing it expects is a single quote. So it will match
javascript:window.open'fum';
but not
javascript:window.open('fum');
One thing you have to learn, is that in Perl, "\(" is the same thing as "(" you're just telling Perl that you want a literal '(' in the string. In order to get lasting escapes, you need to double them.
my $pattern = "javascript:window.open\\('([^']+)'\\);";
my $regex = qr/$pattern/;
Actually preserves the literal ( and yields:
(?-xism:javascript:window.open\('([^']+)'\);)
Which is what I think you want.
As for your question, you should always test the results of a match before using it.
if ( $mech->content =~ m/($pattern)/ ) {
print $1;
}
makes much more sense. And if you want to see it regardless, then it's already implicit in that idea that it might not have a value. i.e., you might not have matched anything. In that case it's best to put alternatives
$mech->content =~ m/($pattern)/;
print $1 || 'UNDEF!';
However, I prefer to grab my captures in the same statement, like so:
my ( $open_arg ) = $mech->content =~ m/($pattern)/;
print $open_arg || 'UNDEF!';
The parens around $open_arg puts the match into a "list context" and returns the captures in a list. Here I'm only expecting one value, so that's all I'm providing for.
Finally, one of the root causes of your problems is that you do not need to specify your expression in a string in order for your regex to be "portable". You can get perl to pre-compile your expression. That way, you only care what instructions the characters are to a regex and not whether or not you'll save your escapes until it is compiled into an expression.
A compiled regex will interpolate itself into other regexes properly. Thus, you get a portable expression that interpolates just as well as a string--and specifically correctly handles instructions that could be lost in a string.
my $pattern = qr/javascript:window.open\('([^']+)'\);/;
Is all that you need. Then you can use it, just as you did. Although, putting parens around the whole thing, would return the whole matched expression (and not just what's between the quotes).
You do not need the parentheses in the match pattern. It will match the whole pattern and return that as $1, which I am guess is not matching, but I am only guessing.
$mech->content =~ m/$pattern/;
or
$mech->content =~ m/(?:$pattern)/;
These are the clustering, non-capturing parentheses.
The way you are doing it is correct.
The solutions have been already given, I'd like to point out that the window.open call might have multiple parameters included in "" and grouped by comma like:
javascript:window.open("http://www.javascript-coder.com","mywindow","status=1,toolbar=1");
There might be spaces between the function name and parentheses, so I'd use a slighty different regex for that:
my $pattern = qr{
javascript:window.open\s*
\(
([^)]+)
\)
}x;
print $1 if $text =~ /$pattern/;
Now you have all parameters in $1 and can process them afterwards with split /,/, $stuff and so on.
It reports an uninitialized value because $1 is undefined. $1 is undefined because you have created a nested matching group by wrapping a second set of parentheses around the pattern. It will also be undefined if nothing matches your pattern.

How can I capture multiple matches from the same Perl regex?

I'm trying to parse a single string and get multiple chunks of data out from the same string with the same regex conditions. I'm parsing a single HTML doc that is static (For an undisclosed reason, I can't use an HTML parser to do the job.) I have an expression that looks like:
$string =~ /\<img\ssrc\="(.*)"/;
and I want to get the value of $1. However, in the one string, there are many img tags like this, so I need something like an array returned (#1?) is this possible?
As Jim's answer, use the /g modifier (in list context or in a loop).
But beware of greediness, you dont want the .* to match more than necessary (and dont escape < = , they are not special).
while($string =~ /<img\s+src="(.*?)"/g ) {
...
}
#list = ($string =~ m/\<img\ssrc\="(.*)"/g);
The g modifier matches all occurences in the string. List context returns all of the matches. See the m// operator in perlop.
You just need the global modifier /g at the end of the match. Then loop through
until there are no matches remaining
my #matches;
while ($string =~ /\<img\ssrc\="(.*)"/g) {
push(#matches, $1);
}
Use the /g modifier and list context on the left, as in
#result = $string =~ /\<img\ssrc\="(.*)"/g;