I found it strange that backreferences ($1,$2,$3) were not working in my original code, so I ran this example from the web:
#!/usr/bin/perl
# matchtest2.plx
use warnings;
use strict;
$_ = '1: A silly sentence (495,a) *BUT* one which will be useful. silly (3)';
my $pattern = "silly";
if (/$pattern/) {
print "The text matches the pattern '$pattern'.\n";
print "\$1 is '$1'\n" if defined $1;
print "\$2 is '$2'\n" if defined $2;
print "\$3 is '$3'\n" if defined $3;
print "\$4 is '$4'\n" if defined $4;
print "\$5 is '$5'\n" if defined $5;
}
else {
print "'$pattern' was not found.\n";
}
Which only gave me:
The text matches the pattern 'silly'.
Why are the backreferences still undefined after the pattern was found? I am using Wubi (Ubuntu 12.04 64-bit) and my perl version is 5.14.2. Thank you in advance for your help.
You are not capturing any strings: No parentheses in your pattern. If you had done:
my $pattern = "(silly)";
You would have gotten something in $1.
In case you do not know, $1 is the text captured in the first parentheses, $2 the second parentheses, and so on.
This is expected behaviour! It is obvious that you pattern will match, so it is no suprise that the corresponding if-block is executed.
The term “backreferences” for $1, $2, ... may be slightly suboptimal, let's call them “capture groups”.
In a regex, you can enclose parts of the pattern with parens to be remembered later:
/(silly)/
This pattern has one group. The contents of this group will be stored in $1 if it matches.
All capture group variables for groups that don't exists in the pattern or were not populated are set to undef on an otherwise successfull match, so for above pattern $2, $3, ... would all be undef.
Related
I have a variable $string and i want to print all the lines after I find a keyword in the line (including the line with keyword)
$string=~ /apple /;
I'm using this regexp to find the key word but I do not how to print lines after this keyword.
It's not really clear where your data is coming from. Let's assume it's a string containing newlines. Let's start by splitting it into an array.
my #string = split /\n/, $string;
We can then use the flip-flop operator to decide which lines to print. I'm using \0 as a regex that is very unlikely to match any string (so, effectively, it's always false).
for (#string) {
say if /apple / .. /\0/;
}
Just keep a flag variable, set it to true when you see the string, print if the flag is true.
perl -ne 'print if $seen ||= /apple/'
If your data in scalar variable we can use several methods
Recommended method
($matching) = $string=~ /([^\n]*apple.+)/s;
print "$matching\n";
And there is another way to do it
$string=~ /[^\n]*apple.+/s;
print $&; #it will print the data which is match.
If you reading the data from file, try the following
while (<$fh>)
{
if(/apple/)
{
print <$fh>;
}
}
Or else try the following one liner
perl -ne 'print <> and exit if(/apple/);' file.txt
My code is below:
use strict;
my $store = 'Media Markt';
my $sentence = "I visited [store]";
# Replace characters "[" and "]"
$sentence =~ s/\[/\$/g;
$sentence =~ s/\]//g;
print $sentence;
I see following at screen:
I visited $store
Is it possible to see following? I want to see value of $store:
I visited Media Markt
You seem to be thinking of using a string, 'store', in order to build a variable name, $store. This gets to the subject of symbolic references, and you do not want to go there.
One way to do what you want is to build a hash that relates such strings to corresponding variables. Then capture the bracketed strings in the sentence and replace them by their hash values
use warnings;
use strict;
my $store = 'Media Markt';
my $time = 'morning';
my %repl = ( store => $store, time => $time );
my $sentence = "I visited [store] in the [time]";
$sentence =~ s/\[ ([^]]+) \]/$repl{$1}/gex;
print "$sentence\n";
This prints the line I visited Media Markt in the morning
The regex captures anything between [ ], by using the negated character class [^]] (any char other than ]), matched one-or-more times (+). Then it replaces that with its value in the hash, using /e to evaluate the replacement side as an expression. Since brackets are matched as well they end up being removed. The /x allows spaces inside, for readibilty.
For each string found in brackets there must be a key-value pair in the hash or you'll get a warning. To account for this, we can provide an alternative
$sentence =~ s{\[ ([^]+) \]}{$repl{$1}//"[$1]"}gex;
The defined-or operator (//) puts back "[$1]" if $repl{$1} returns undef (no key $1 in the hash, or it has undef value). Thus strings which have no hash pairs are unchanged. I changed the delimiters to s{}{} so that // can be used inside.
This does not allow nesting (like [store [name]]), does not handle multiline strings, and has other limitations. But it should work for reasonable cases.
As I told you on the Perl Programmers Facebook group, this is very similar to one of the answers in the Perl FAQ.
How can I expand variables in text strings?
If you can avoid it, don't, or if you can use a templating system, such as Text::Template or Template Toolkit, do that instead. You might even be able to get the job done with sprintf or printf:
my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a full templating system, I'll use a string that has two Perl scalar variables in it. In this example, I want to expand $foo and $bar to their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
$string = 'Say hello to $foo and $bar';
One way I can do this involves the substitution operator and a double /e flag. The first /e evaluates $1 on the replacement side and turns it into $foo. The second /e starts with $foo and replaces it with its value. $foo, then, turns into 'Fred', and that's finally what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The /e will also silently ignore violations of strict, replacing undefined variable names with the empty string. Since I'm using the /e flag (twice even!), I have all of the same security problems I have with eval in its string form. If there's something odd in $foo, perhaps something like #{[ system "rm -rf /" ]}, then I could get myself in trouble.
To get around the security problem, I could also pull the values from a hash instead of evaluating variable names. Using a single /e, I can check the hash to ensure the value exists, and if it doesn't, I can replace the missing value with a marker, in this case ??? to signal that I missed something:
my $string = 'This has $foo and $bar';
my %Replacements = (
foo => 'Fred',
);
# $string =~ s/\$(\w+)/$Replacements{$1}/g;
$string =~ s/\$(\w+)/
exists $Replacements{$1} ? $Replacements{$1} : '???'
/eg;
print $string;
And the actual (but really not recommended - for the reasons explained in the FAQ above) answer to your question is:
$sentence =~ s/\[(\w+)]/'$' . $1/ee;
I have a subroutine in Perl sub findfiles , I have to pass a quoted value "*/*" as input parameter since it complains without quoting ,on the other hand in my subroutine I needed it to be unquoted (may be!)The problem is when I print the value to check ,I don't see any quote,or any thing but may be there are some special hidden character or something I don't know ? My codes work properly when I use */*directly but not when I pass it as as an input parameter
Do you have any idea?
sub findfiles {
$dirname=$_[0];
my #temp = grep {-f} <$dirname>;
print #temp;
}
&findfiles("*/*"); doesnot work
but
sub findfiles {
$dirname=$_[0];
my #temp = grep {-f} <*/*>;
print #temp;
}
does its job
With your updated code, I can see where your error lies. While
my #temp = grep {-f} <*/*>;
Works as a glob
my #temp = grep {-f} <$dirname>;
Is interpreted as a readline() on the file handle $dirname.
If you want to avoid ambiguity you can use the function for glob:
my #temp = grep -f, glob $dirname;
You might also be interested in using File::Find, which finds files recursively.
NOTE: This problem could have been avoided if you had warnings turned on. As a rule of thumb, coding in perl without using
use strict;
use warnings;
...is a very bad idea. These two pragmas will help you identify problems with your code.
The problem is when I print the value to check ,I don't see any quote
$test="*/*"
^string delimiter
^^^string
^string delimiter
When you print a string (be it from a string literal, a scalar or whatever) you print the string.
The delimiters don't get printed. They just tell perl where the edges of the data are.
Do you know about File::Find?
use File::Find ();
File::Find::find( sub { say $File::Find::name if -f; } => $my_root );
Or what about File::Find::Rule (see file)?
say foreach File::Find::Rule->file->in( $my_root );
I've been doing some searching and haven't found an answer. Why isn't this working?
$self->{W_CONTENT} =~ /$regex/;
print $1; #is there a value? YES
$store{URL} =~ s/$param/$1/;
Yes $1 has a value. $param is replaced however it is replaced with nothing. I'm positive $1 has a value. If I replace with text instead of "$1" it works fine. Please help!
For $1 to have a value you need to ensure that $param has parentheses () in it. i.e. The following has a problem similar to what you are explaining.
my $fred = "Fred";
$fred =~ s/red/$1/;
# $fred will now be "F"
But this works
my $fred = "Fred";
$fred =~ s/r(ed)/$1/;
# $fred will now be "Fed"
Now if you want to use the $1 from your first regex in the second one you need to copy it. Every regex evaluation resets $1 ... $&. So you want something like:
$self->{W_CONTENT} =~ /$regex/;
print $1; #is there a value? YES
my $old1 = $1;
$store{URL} =~ s/$param/$old1/;
Backreferences such as $1 shouldn't be used inside the expression; you'd use a different notation - for an overview, check out Perl Regular Expressions Quickstart.
Consider getting the value of $1 and storing it in another variable, then using that in the regex.
How can I write this with the smartmatch operator (~~)?
use 5.010;
my $string = '12 23 34 45 5464 46';
while ( $string =~ /(\d\d)\s/g ) {
say $1;
}
Interesting. perlsyn states:
Any ~~ Regex pattern match $a =~ /$b/
so, at first glance, it seems reasonable to expect
use strict; use warnings;
use 5.010;
my $string = '12 23 34 45 5464 46';
while ( $string ~~ /(\d\d)\s/g ) {
say $1;
}
to print 12, 23, etc but it gets stuck in a loop, matching 12 repeatedly. Using:
$ perl -MO=Deparse y.pl
yields
while ($string ~~ qr/(\d\d)\s/g) {
say $1;
}
looking at perlop, we notice
qr/STRING/msixpo
Note that 'g' is not listed as a modifier (logically, to me).
Interestingly, if you write:
my $re = qr/(\d\d)\s/g;
perl barfs:
Bareword found where operator expected at C:\Temp\y.pl line 5,
near "qr/(\d\d)\s/g"
syntax error at C:\Temp\y.pl line 5, near "qr/(\d\d)\s/g"
and presumably it should also say something if an invalid expression is used in the code above
If we go and look at what these two variants get transformed into, we can see the reason for this.
First lets look at the original version.
perl -MO=Deparse -e'while("abc" =~ /(.)/g){print "hi\n"}'
while ('abc' =~ /(.)/g) {
print "hi\n";
}
As you can see there wasn't any changing of the opcodes.
Now if you go and change it to use the smart-match operator, you can see it does actually change.
perl -MO=Deparse -e'while("abc" ~~ /(.)/g){print "hi\n"}'
while ('abc' ~~ qr/(.)/g) {
print "hi\n";
}
It changes it to qr, which doesn't recognize the /g option.
This should probably give you an error, but it doesn't get transformed until after it gets parsed.
The warning you should have gotten, and would get if you used qr instead is:
syntax error at -e line 1, near "qr/(.)/g"
The smart-match feature was never intended to replace the =~ operator. It came out of the process of making given/when work like it does.
Most of the time, when(EXPR) is treated as an implicit smart match of $_.
...
Is the expected behaviour to output to first match endlessly? Because that's what this code must do in its current form. The problem isn't the smart-match operator. The while loop is endless, because no modification ever occurs to $string. The /g global switch doesn't change the loop itself.
What are you trying to achieve? I'm assuming you want to output the two-digit values, one per line. In which case you might want to consider:
say join("\n", grep { /^\d{2}$/ } split(" ",$string));
To be honest, I'm not sure you can use the smart match operator for this. In my limited testing, it looks like the smart match is returning a boolean instead of a list of matches. The code you posted (using =~) can work without it, however.
What you posted doesn't work because of the while loop. The conditional statement on a while loop is executed before the start of each iteration. In this case, your regex is returning the first value in $string because it is reset at each iteration. A foreach would work however:
my $string = '12 23 34 45 5464 46';
foreach my $number ($string =~ /(\d\d)\s/g) {
print $number."\n";
}