What does =~ m/\#F/ mean in Perl? - regex

open DMLOG, "<dmlog.txt" or &error("Can't open log file: $!");
chomp(#entirelog=<DMLOG>);
close DMLOG;
for $line (#entirelog)
{
if ($line =~ m/\#F/)
{
$titlecolumn = $line;
last;
}
}
I found that =~ is a regular expression I think, but I don't quite understand what its doing here.

It assigns the first line to $titlecolumn that contains an # followed by an F.
The =~ is the bind operator and applies a regex to a string. That regex would usually be written as /#F/. The m prefix can be used to emphasize that the following literal is a prefix (important when other delimiters are used).

Do you understand what regular expressions are? Or, is the =~ throwing you off?
In most programming languages, you would see something like this:
if ( regexp(line, "/#F/") ) {
...
}
However, in Perl, regular expressions are inspired by Awk's syntax. Thus:
if ( $line =~ /#F/ ) {
...
}
The =~ means the regular expression will act upon the variable name on the left. If the pattern #F is found in $line, the if statement is true.
You might want to look at the Regular Expression Tutorial if you're not familiar with them. Regular expressions are extremely powerful and very commonly used in Perl. In fact, they tend to be very used in Perl and is one of the reasons developers in other languages will claim that Perl is a Write Only language.

It is called Binding Operator. It is used to match pattern on RHS with the variable on LHS. Similarly you have got !~ which negates the matching.
For your particular case:
$line =~ m/\#F/
This test whether the $line matches the pattern - /#F/.

Yes, =~ is the binding operator binding an expression to the pattern match m//.
The if statement checks, if a line matches the given regular expression. In this case, it checks, if there is a hash-sign followed by a capital F.
The backslash has just been added (maybe) to avoid treating # as a comment sign (which isn't needed).

Related

Regular expression chaining/mixing in perl

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.

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 is "Search pattern not terminated" in this snippet?

I am trying to update a piece of code to remove any non-alphanumeric character, assign the resultant string to a new variable, and rewrite my HTML to include that value in a new meta tag:
if ( $main::url =~ m/index:Devices/ )
{
my $prodname = getMetaValue(\$doc,'Product_Name');
$prodname =~ tr/[^a-zA-Z0-9 ];
$strippedname =~ $prodname;
$doc =~ s{</head>}{<meta name='Stripped_Name' content='$strippedname' />\n</head>}is;
}
The last line throws a "Search pattern not terminated" error, and I can't figure out why. I use a similar method that does work elsewhere in the script:
if ( $main::url =~ m/index:Devices/ )
{
my $prodname = getMetaValue(\$doc,'Product_Name');
my $brandname = getMetaValue(\$doc,'Manufacturer_Name');
my $devicefullname = $brandname.' '.$prodname;
$doc =~ s{</head>}{<meta name='Device_Full_Name' content='$devicefullname' />\n</head>}is;
}
Any idea why the special character removal script fails me?
Thank you!
The syntax of the tr operator is tr/CHARS/REPLACEMENT/. Further, it performs a transliteration (not regex match) which normally replaces the given literal characters, and in a rather particular way.
But you can do what you want with tr, as it allows ranges and has /c modifier (complement)
$prodname =~ tr/a-zA-Z0-9 //dc;
From Quote-Like-Operators in perlop
If the /c modifier is specified, the SEARCHLIST character set is complemented.
However, using tr/// (specially with /c) is a bit obscure in comparison with using s///, which you also utilize later in the code. Use of s/// would make it clearer
$prodname =~ s/[^a-zA-Z0-9 ]//g;
The modifier /g makes it remove all occurences of characters specified by [^...].
The regex itself can also be written as
s/[^a-z0-9 ]//gi;
but see Negation in perlrecharclass for notes on using /i with negated class and unicode. For an efficiency improvement we can add the + quantifier, s/[...]+//gi, as all occurences need be removed anyway. Note that the tr/// should be much faster here.
With POSIX character classes this can be written as s/[^[:alnum:] ]//g;
tr/// needs three instances of the delimiter, not just one.
$prodname =~ tr/[^a-zA-Z0-9 ];
Moreover, [ means the literal square bracket in tr. Maybe you wanted m// or s///?

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.)

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.