Using perl, i'm unable to perform a simple search to see if one number is within another. The below example doesn't enter the if statement.
my $a = "12345";
my $b = "123456789";
if($a=~ m/$b/g) #doesn't work
{
print "success";
}
The below doesn't work either.
my $a = "12345";
my $b = "123456789";
if($a =~ /$b/) #doesn't work
{
print "success";
}
To check if $substr is in $string, you want:
if ($string =~ /\Q$substr\E/)
or
if (index($string, $substr) >= 0)
In your case, that means
if ($b =~ /\Q$a\E/)
or
if (index($b, $a) >= 0)
\Q..\E causes the contents of the variable in between to be matched literally rather than have it treated as a regex pattern.
if (//g) makes no sense, and using it can produce subtle and very odd problems.
I believe you might have mixed up your variables in the statement. The $a =~ m/$b/g indicates that $b can be found in $a (in order to be true), not the $a can be found in $b. Since $a is less in length than $b, the statements fail ( since 123456789 cannot be found in 12345).
I suspect what you want is:
if ( $b =~ m/$a/g ) # or ( $b =~ /$a/ )
{
print "success";
}
There are characters that may have different meanings in regular expressions. Instead, do this:
if (index($b, $a) != -1) #works
Related
I was wondering if there is an easy/clean way of swapping values as follows, perhaps using a single regex/substitution?
If $a ends with "x", substitute it with "y". And similarly if $a ends with "y", swap it with "x":
$a = "test_x";
if ($a =~ /x$/) {
$a =~ s/x$/y/;
} else {
$a =~ s/y$/x/;
}
I can only think of something like this:
$a = $a =~ /x$/ ? s/x$/y/ : s/y$/x/;
This is simply:
$a =~ s/x$/y/ or $a =~ s/y$/x/;
It's almost always redundant to do a match to see if you should do a substitution.
Another way:
substr($a,-1) =~ y/xy/yx/;
You can squeeze it in a line like you show, perhaps a bit nicer with /r (with v5.14+).
Or you can prepare a hash. This also relieves the code from hard-coding particular characters.
my %swap = (x => 'y', y => 'x', a => 'b', b => 'a'); # expand as needed
my #test = map { 'test_' . $_ } qw(x y a b Z);
for my $string (#test)
{
$string =~ s| (.)$ | $swap{$1} // $1 |ex;
say $string;
}
The // (defined-or) is there to handle the case where the last character isn't in the hash, in which case $swap{$1} returns undef. Thanks to user52889 for the comment.
To swap individual characters, you can use tr///.
Not sure what your criteria for cleanliness or ease, but you could even do this inside the right hand side of the substitution:
$xy = "test_x" =~ s`([xy])$`$1=~tr/xy/yx/r`re; # $xy is "test_y"
I need to grep a value from an array.
For example i have a values
#a=('branches/Soft/a.txt', 'branches/Soft/h.cpp', branches/Main/utils.pl');
#Array = ('branches/Soft/a.txt', 'branches/Soft/h.cpp', branches/Main/utils.pl','branches/Soft/B2/c.tct', 'branches/Docs/A1/b.txt');
Now, i need to loop #a and find each value matches to #Array. For Example
It works for me with grep. You'd do it the exact same way as in the More::ListUtils example below, except for having grep instead of any. You can also shorten it to
my $got_it = grep { /$str/ } #paths;
my #matches = grep { /$str/ } #paths;
This by default tests with /m against $_, each element of the list in turn. The $str and #paths are the same as below.
You can use the module More::ListUtils as well. Its function any returns true/false depending on whether the condition in the block is satisfied for any element in the list, ie. whether there was a match in this case.
use warnings;
use strict;
use Most::ListUtils;
my $str = 'branches/Soft/a.txt';
my #paths = ('branches/Soft/a.txt', 'branches/Soft/b.txt',
'branches/Docs/A1/b.txt', 'branches/Soft/B2/c.tct');
my $got_match = any { $_ =~ m/$str/ } #paths;
With the list above, containing the $str, the $got_match is 1.
Or you can roll it by hand and catch the match as well
foreach my $p (#paths) {
print "Found it: $1\n" if $p =~ m/($str)/;
}
This does print out the match.
Note that the strings you show in your example do not contain the one to match. I added it to my list for a test. Without it in the list no match is found in either of the examples.
To test for more than one string, with the added sample
my #strings = ('branches/Soft/a.txt', 'branches/Soft/h.cpp', 'branches/Main/utils.pl');
my #paths = ('branches/Soft/a.txt', 'branches/Soft/h.cpp', 'branches/Main/utils.pl',
'branches/Soft/B2/c.tct', 'branches/Docs/A1/b.txt');
foreach my $str (#strings) {
foreach my $p (#paths) {
print "Found it: $1\n" if $p =~ m/($str)/;
}
# Or, instead of the foreach loop above use
# my $match = grep { /$str/ } #paths;
# print "Matched for $str\n" if $match;
}
This prints
Found it: branches/Soft/a.txt
Found it: branches/Soft/h.cpp
Found it: branches/Main/utils.pl
When the lines with grep are uncommented and foreach ones commented out I get the corresponding prints for the same strings.
The slashes dot in $a will pose a problem so you either have to escape them it when doing regex match or use a simple eq to find the matches:
Regex match with $a escaped:
my #matches = grep { /\Q$a\E/ } #array;
Simple comparison with "equals":
my #matches = grep { $_ eq $a } #array;
With your sample data both will give an empty array #matches because there is no match.
This Solved My Question. Thanks to all especially #zdim for the valuable time and support
my #SVNFILES = ('branches/Soft/a.txt', 'branches/Soft/b.txt');
my #paths = ('branches/Soft/a.txt', 'branches/Soft/b.txt',
'branches/Docs/A1/b.txt', 'branches/Soft/B2/c.tct');
foreach my $svn (#SVNFILES)
{
chomp ($svn);
my $m = grep { /$svn/ } (#paths);
if ( $m eq '0' ) {
print "Files Mismatch\n";
exit 1;
}
}
You should escape characters like '/' and '.' in any regex when you need it as a character.
Likewise :
$a="branches\/Soft\/a\.txt"
Retry whatever you did with either grep or perl with that. If it still doesn't work, tell us precisely what you tried.
I have an array contain #arr = { "a=b", "a>b", "a<b", "a!=b", "a-b" }. What is the best way to get a and b with any operator between. I can extract by
for($i=0; $i<=$#arr; $i++){
$str = $arr[$i];
if($str =~ m/^(.*?)(\s*=\s*)(.*)(;)/g){
my $d = $1;
my $e = $3;
}
Follow by all if statement with the possible operator like "!=", "<" etc. But this will make my code look messy. Any better solution for this?
You could try something like this one liner
perl -e '#a = ("a=b","a>b","a<b","a!=b","a-b"); for $l (#a) { $l =~ s/(.).*(.)/$1/; print "$1$2\n"};'
The key thing is the greedy match ie "(.*)" between the two single character matches ie "(.)". To really make sure that you start at the start and end of the strings you could use this
perl -e '#a = ("a=b","a>b","a<b","a!=b","a-b"); for $l (#a) { $l =~ s/^(.).*(.)$/$1/; print "$1$2\n"};'
A complete working example that demonstrates the whole thing would be
#!/usr/bin/perl
use strict;
use warnings;
my #expressions = ("a=b","a>b","a<b","a!=b","a-b");
for my $exp (#expressions) {
$exp =~ s/^(.).*(.)$/$1$2/;
print "$1$2 is the same as $exp\n";
};
A very simple regex might be
/^(\w+)\s*(\W+)\s*(\w+)$/
Or you enumerate possible operators
/^(\w+)\s*(=|!=|<|>|<=|>=|\+|-|\*|\/|==)\s*(\w+)$/
It depends whether the input can be trusted or not. If not, you might have to be more meticulous w.r.t. the identifiers, too. Here's a simpler loop, and no need to use m//g(lobal). Not sure about the semicolon - omitted it.
my #arr = ( "a=b", "a>b", "a<b", "a!=b", "a-b" );
for my $str (#arr){
if($str =~ /^(\w+)\s*(=|!=|<|>|<=|>=|\+|-|\*|\/|==)\s*(\w+)$/ ){
my $d = $1;
my $e = $3;
print "d=$d e=$e\n";
}
}
Later If you enumerate the operators, you can also add word symbols:
if($str =~ /^(\w+)\s*(=|!=|<|>|<=|>=|\+|-|\*|\/|==|x?or|and)\s*(\w+)$/ ){
...
if there always 'a' and 'b' at the beginning and the end you could try:
my $str = 'a<b';
my( $op ) = $str =~ /^a(.*)b$/;
Not a well thought out answer. Will reconsider the problem.
The loop fails. What is wrong with the array?
I would like the regex to return B when it parses the first string, and M when it parses the second string.
How is such an regex constructed?
#!/usr/bin/perl
use warnings;
use strict;
my $a = "0.0 B/s";
my $b = "12.0 MiB/s";
while (defined (my $s = shift ("$a", "$b"))) {
my $unit = $1 if ($a =~ m/.*([KMGT])i?B\/s$/);
print "$unit\n";
}
shift is meant to be used with arrays, not lists. If you want to use a while loop, you need to pre-declare an array containing $a and $b (which, by the way, are a bad choice for variable names).
Having said that, a for loop construct is the more natural choice here:
for my $s ( $var1, $var2 ) { ... }
And given that you're trying to extract the measurement unit, why not do things a slightly different way:
say for map { my ( $s ) = /$regex/; $s } $var1, $var2;
You need another substitution:
for ($a, $b) {
if (m!((?:[KMGT]i)?B)/s$!) {
my $unit = $1;
$unit =~ s/(.).*/$1/;
print "$unit\n" if $unit;
}
}
Your while has issues.
You are using variable $a inside loop, when you want to use $s.
I'd use it this way:
#!/usr/bin/perl
use warnings;
use strict;
my $a = "0.0 B/s";
my $b = "12.0 MiB/s";
foreach my $s($a, $b) {
print $1 if ($s =~ m/.*([KMGT])i?B\/s$/);
}
In Perl, how to write a regular expression that replaces only up to N matches per string?
I.e., I'm looking for a middle ground between s/aa/bb/; and s/aa/bb/g;. I want to allow multiple substitutions, but only up to N times.
I can think of three reliable ways. The first is to replace everything after the Nth match with itself.
my $max = 5;
$s =~ s/(aa)/ $max-- > 0 ? 'bb' : $1 /eg;
That's not very efficient if there are far more than N matches. For that, we need to move the loop out of the regex engine. The next two methods are ways of doing that.
my $max = 5;
my $out = '';
$out .= $1 . 'bb' while $max-- && $in =~ /\G(.*?)aa/gcs;
$out .= $1 if $in =~ /\G(.*)/gcs;
And this time, in-place:
my $max = 5;
my $replace = 'bb';
while ($max-- && $s =~ s/\G.*?\Kaa/$replace/s) {
pos($s) = $-[0] + length($replace);
}
You might be tempted to do something like
my $max = 5;
$s =~ s/aa/bb/ for 1..$max;
but that approach will fail for other patterns and/or replacement expressions.
my $max = 5;
$s =~ s/aa/ba/ for 1..$max; # XXX Turns 'aaaaaaaa'
# into 'bbbbbaaa'
# instead of 'babababa'
And of course, starting from the beginning of the string every time could be expensive.
What you want is not posible in regular expressions. But you can put the replacement in a for-loop:
my $i;
my $aa = 'aaaaaaaaaaaaaaaaaaaa';
for ($i=0;$i<4;$i++) {
$aa =~ s/aa/bb/;
}
print "$aa\n";
result:
bbbbbbbbaaaaaaaaaaaa
You can use the /e flag which evaluates the right side as an expression:
my $n = 3;
$string =~ s/(aa)/$n-- > 0 ? "bb" : $1/ge;
Here's a solution using the /e modifier, with which you can use
perl code to generate the replacement string:
my $count = 0;
$string =~ s{ $pattern }
{
$count++;
if ($count < $limit ) {
$replace;
} else {
$&; # faking a no-op, replacing with the original match.
}
}xeg;
With perl 5.10 or later you can drop the $& (which has weird
performance complications) and use ${^MATCH} via the /p modifier
$string =~ s{ $pattern }
{
$count++;
if ($count < $limit ) {
$replace;
} else {
${^MATCH};
}
}xegp;
It's too bad you can't just do this, but you can't:
last if $count >= $limit;