Perl conditional regex with inequalities - regex

I have one query. I have to match 2 strings in one if condition:
$release = 5.x (Here x should be greater than or equal to 3)
$version = Rx (this x should be greater than or equal to 5 if $release is 5.3, otherwise anything is acceptable)
e.g. 5.1R11 is not acceptable, 5.3R4 is not, 5.3R5 is acceptable, and 5.4 R1 is acceptable.
I have written a code like this:
$release = "5.2";
$version = "R4";
if ( $release =~ /5.(?>=3)(\d)/ && $version =~ m/R(?>=5)(\d)/ )
{
print "OK";
}
How can I write this?

This is really a three-level version string, and I suggest that you use Perl's version facility
use strict;
use warnings 'all';
use feature 'say';
use version;
my $release = '5.2';
my $version = 'R4';
if ( $version =~ /R(\d+)/ && version->parse("$release.$1") ge v5.3.5 ) {
say 'OK';
}

In regex (?>) it means atomic grouping.
Group the element so it will stored into $1 then compare the $1 with number so it should be
if (( ($release =~ /5\.(\d)/) && ($1 > 3) ) && (($version =~ m/R(\d)/) && ($1 >= 3) ) )
{
print "OK\n";
}

I got the correct one after modifying mkhun's solution:
if ((($release =~ /5.3/)) && (($version =~ m/R(\d+)(.\d+)?/) && ($1 >= 5))
|| ((($release =~ /5.(\d)/) && ($1 > 3)) && ($version =~ m/R(\d+)(.\d+)?/)) )
{
print "OK\n";
}

Related

Search pattern for decoding logic

I need to decode logic from a database I reference when determining if an attribute is able to be associated with an item I am defining. The database uses standard logic flow and order of importance. I need to be able to determine nested conditional statements in the strings I pull from the database. For example:
$val = "(red && (blue || yellow)) && black";
The string I'd pull from the database would resemble:
$pull = "red.yellow.black";
I would separate $pull on . and store the values in an array, then I'd verify each value is in the array(s) I generate from the $val string. I am having trouble with the best method for determining how to unpack the nested logic though. Initially, I considered just using a regex, then removing the portion of the logic that has been assessed like so:
($eval) = $val =~ /.*\((.*)?\)/; $val =~ s/(.*)\((.*)?\)(.*)/$1 $2/;
If I do this in a while ($val =~ /\(/) loop, I could probably stack the extracted logic into arrays and evaluate the logical expression in each element to determine if each condition is true for the item I am evaluating, but there's something wrong with my regexp causing the sequence to fail. The ? is not as lazy as I thought, apparently...
my $val = "(red && (blue || yellow)) && black";
my $pull = "red.yellow.black";
my #stack = ();
until ($val !~ /\(/) {
my ($eval) = $val =~ /.*\((.*)?\)/;
push(#stack, $eval);
print "$eval\n";
$val =~ s/(.+)\((.*)?\)(.*)/$1 $2/;
print "$val\n";
}
If I just run the sequence in a perl shell, with some debugging info, I get this:
[bin]$ perl -e 'my $val = "((red && (blue || yellow) && black)"; my $pull = "red.yellow.black"; my #stack = (); until ($val !~ /\(/) { my ($eval) = $val =~ /.*\((.+)?\).?/; push(#stack, $eval); print "EVAL: $eval\n";$pause = <STDIN>;$val =~ s/(.*)\((.*)?\)(.*)/$1 $2/;print "VAL: $val\n"; }'
EVAL: blue || yellow) && black
VAL: ((red && blue || yellow) && black
EVAL: red && blue || yellow
VAL: ( red && blue || yellow
EVAL:
Any input on what I'm doing wrong would be appreciated, and any improvements on efficiency would be greatly appreciated! TIA
Update: Ok, so I just dumbed the whole thing down like this. I lose some of the order of operations, but if I evaluate each statement individually, whenever one of the conditions is broken, I'll pick it up and be able to act on the info accordingly. The code I am going to expand on is below. This would just return the individual components of the expression. I don't like how everything ouside parenthesis is concatenated into the first element, but I guess it still works fundamentally.
my $val = "(red && (blue || yellow)) && black";
$val =~ s/\s+//g;
my #chars = ();
my #ops = ();
while ($val) {
my ($char) = $val =~ /(.)/;
$val =~ s/.//;
push #chars, $char;
}
my $index = 0;
foreach my $char (#chars) {
if ($char =~ /\(/) {
$index++;
} elsif ($char =~ /\)/) {
$index--
}
#assign the character to the index.
$ops[$index] .= $char;
}
s/[()]//g for #ops;
print " #ops\n";
Output:
[/bin]$ perl decode_logic.pl
&&black red&& blue||yellow

Perl Regex replace all domain except a particular in a string

I have a string of domain like below:
$string = 'https://code.google.com && http://mycode.com/data && times.com && https://thehindu.com';
I want to replace all domains except mycode.com with mycode.com/unknown
So the output of above string after applying regex should be:
https://mycode.com/unknown && http://mycode.com/data && mycode.com/unknown && https://mycode.com/unknown
I have tried below regex, but it changes http://mycode.com/data also:
$string =~ s/(?<!mycode)[a-z\.]+?\.(com|org|net)/mycode\.com\/unknown/g;
How should i modify my regex to not match mycode.com
$s =~ s{
( [a-z.]+\.(?:com|org|net) )
(?![a-z.])
}{
$1 eq "mycode.com" ? $1 : "mycode.com/unknown"
}xeg;
or
$s =~ s{
(?<![a-z.])
(?! mycode\.com (?![a-z.]) )
([a-z.]+\.(?:com|org|net) (?![a-z.])
}{mycode.com/unknown}xg;
Handles
mycoder.com
mycode.combo.com
mycode.combo
notmycode.com
foo.combo
You're really closed, instead of lookbehind, use lookahead:
my $string = 'https://code.google.com && http://mycode.com/data && times.com && https://thehindu.com';
$string =~ s~(?<![a-z.])(?!mycode)[a-z.]+\.(?:com|org|net)~mycode.com/unknown~g;
say $string;
Output:.
https://mycode.com/unknown && http://mycode.com/data && mycode.com/unknown && https://mycode.com/unknown
Try replacing the matching part of the regex by that :
(http:\/\/)?(?!(mycode\.|ycode\.|code\.|ode\.|de\.|e\.))[a-z\.]+?\.(com|org|net)
Please try something like this
$string =~ s/https?:\/\/([^\/\s]+)/$match=$1;($match!~\/mycode.com\/)?'https:\/\/mycode.com\/unknown':$match/eg;
(Ignoring url without http://... and using 3 non-scrolling lines)
my $s = 'https://code.google.com && ....'
$s =~ s!//(?=mycode.com($|[^.\w]))!\cA!g; # // -> CTR-A
$s =~ s!//(\S+)!//mycode.com/unknown!g;
$s =~ s!\cA!//!g; # CTR-A -> //
Basic idea:
protect/mark/save the special cases
substitute the general situations
put-back the specials

Replace only up to N matches on a line

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;

regex for a number, number incremented by one and two

Give a number x, I wonder if there is any regex that matches for x and x+1 and x+2.
Thanks,
The best approach would probably be to do something like:
my $x = 3;
my $regex = join "|", $x, $x+1, $x+2;
for (0 .. 10) {
print "$_\n" if /$regex/;
}
But if you want, you can use interpolation directly within the regexp:
my $x = 3;
for (0 .. 10) {
print "$_\n" if /$x|${\($x+1)}|${\($x+2)}/;
}
Output for both:
3
4
5
I personally think the latter is a lot less readable though.
String contains:
my $pat = join '|', $x, $x+1, $x+2;
$s =~ /(?<![0-9])(?:$pat)(?![0-9])/ # Assumes non-negative integers
Exact match:
my $pat = join '|', $x, $x+1, $x+2;
$y =~ /^(?:$pat)\z/
$y == $x || $y == $x+1 || $y == $x+2 # Most straightfoward
$x <= $y && $y <= $x+2 # Possibly clearest
Exact match (More exotic):
grep $y == $x + $_, 0..2
$y ~~ [ map $x_+$_, 0..2 ]
You could use (??{...}):
use re qw'eval';
/^ (?: $x | (??{ $x+1 }) | (??{ $x+2 }) ) $/x;
I would like to say that it make more sense to use $":
local $" #" # fix highlighting
= '|';
/^#{[ $x, $x+1, $x+2 ]}$/;
/^#{[ $x .. $x+2 ]}$/;
my #match = ( $x, $x+1, $x+2 );
/^#match$/;
I first thought of using index like so:
index( $source, $x + 2 );
But then, for $x=1, it just looks for a '3' anywhere in the string, matching 31, 23. So it appears that you might want to use a regex to make sure that it's an isolated string of digits.
/(?<!\d)${\( $x + 2 )}(?!\d)/
Given your other variable--let's call it $y--why not just check whether or not $y==$x, $y-$x==1, or $y-$x==2?
As ghoti pointed out in his/her comment, regular expressions aren't the tool for this.
For X = 10, regex should be \b(?:11|12)\b

How to refer regular expression matches evaluated in one statement?

I want to do something like this
if(($Fifo[5]=~/T0int(\S+)/)&&($Fifo[6]=~/T0int(\S+)/)&&($1 ne $2))
{
<Do something>
}
How can I reference matches evaluated in two regexps ?
By $1 I meant match evaluated in the first regexp and $2 in the next.
my($first) = $Fifo[5] =~ /T0int(\S+)/;
my($second) = $Fifo[6] =~ /T0int(\S+)/;
if (defined($first) && defined($second) && $first ne $second)) { ⋯ }
or more cavalierly:
if (($Fifo[5] =~ /T0int(\S+)/)[0] ne ($Fifo[6] =~ /T0int(\S+)/)[0]) { ⋯ }
or even more cavalierly still:
if ( (my($first, $second) = "#Fifo[5,6]" =~ /T0int(\S+)/g )
&& $first && $second
&& $first ne $second)
{
⋯
}
Try something like this:
if( ($Fifo[5] =~ (/T0int(\S+)/)) && ($Fifo[6] =~ (/T0int(\S+)/)) && ($1 ne $2) )
Basically put parenthesis around regex to group them as $1, $2