Is it possible to compare the value of an expression like this in perl?
if(0x100 < $my_var < 0x200) {
return $my_var;
}
The above way gives a syntax error.
In perl, Is there a better way to check if the value is present in specific range?
I don't want to use two if conditions to do this. I am sure,for this, there would a better way in any language.
Sorry if this a very basic question. I am trying to write my first programs in perl.
Edit
if($my_var >0x100 && $my_var < 0x200)
I donot like the above method. Please let me know if there is any other sophisticated way. I would be using it a lot on 64 bit addresses. Hence writing like this, would make the code look ugly. Please suggest other ways.
You can write your own function,
sub between {
my ($x,$y,$z) = #_;
return ($x < $y and $y < $z);
}
if (between(0x100, $my_var, 0x200)) { print "OK"; }
I prefer this itself coz it dont not take much effort if there are two conditions inside an if:
if($my_var >0x100 && $my_var < 0x200)
if($x ~~ 0X100..0X200) will also work but in perl6 not in perl5
If you are not using perl6 then forget about the above method.
You need to break the conditional up in to two separate tests along with an and statement,
like so,
# This example should print "Pass"
my $my_var = 0x150;
if (0x100 < $my_var && $my_var < 0x200) {
print "Pass\n";
} else {
print "Fail\n";
}
This is the most common way of dealing with ranges. For something a bit different that might still prove useful, you an use the smart match operator (aka. inchworm operator)1 along with the range operator which might actually prove more readable when dealing with memory spaces,
# This example should show that 0x150 and 0x190 pass
my #addresses = (0x090, 0x150, 0x190, 0x210);
for my $address (#addresses) {
if ($address ~~ [0x100 .. 0x200]) {
print "$address - Pass\n";
} else {
print "$address - Fail\n";
}
}
Note that smart match is experimental / depercated depending upon which version of Perl you are using. match::smart provides the same functionality.
I would just assign upper and lower values to variables $high $low and do:
if($val > $low && $val < $high)
Related
i am new to perl and using regex. I have a subroutine that is suppose to show that we are looking for a pattern that has one of more dots".". The next line is to use this subroutine to check if there are dots in some patterns. However I find that the if block in my if/else statement is not evaluated, it only evaluates the else block.
my $hostname1 = "hh.uu";
my $hostname2 = "yyhu";
sub isDotted {
return 1 if #_ =~ /\./g; return 0;
}
if (isDotted($hostname1) != isDotted($hostname2))
{
print "Must be equal\n";
}
else {
print "Perfect good to go\n";
}
This code prints out "Perfect good to go" and the fact that $hostname2 does not have a dot "." in it means it should print out "must be equal" but it does not evaluate the if block.
*Update: Sorry I have made the changes, this code should compile
Perl passes arguments to subroutines in a fixed-name array (#_). So you have to extract your argument from that array. By using the bare array name, you are probably in "scalar context" and perl evaluates arrays in scalar context to be their size, 1 in this case. And 1 is never going to match /\./. The way to refer to the first element of array #_ is $_[0]. The /g flag is also superfluous. So try changing that line to this:
return 1 if $_[0] =~ /\./; return 0;
Lots wrong with this code.
1) It does not compile. Use perl -c script_name to check if it compiles correctly.
2) For anything more that a one line script add
use strict;
use warnings;
to the top of the script.
3) Parameters are passed into a subroutine in #_ not $_
4) = ~ is two separate operators you want the =~ operator
5) hostname1 and hostname2 are not the same as $hostname1 and $hostname2, you should be using the latter to pass the values into your subrouteen.
So here is what I'm doing. This is for homework, and I know I can't come on here and get you guys to do my homework for me but I'm stuck. We have to use perl (First time ever using it so forgive my stupidity) to make a function $starts_with that takes a parameter $str0 and $prefix. if $str0 starts with $prefix. then the function returns true. if it doesn't then it isn't pretty simple. We have to use regular expressions because that is the whole point of the exercise so here is my code
sub starts_with
{
$str0 = $_[0];
$prefix = $_[1];
if($prefix =~ /^($str0)/)
{
print $str0."\n";
print m/^(prefix)/."\n";
$startsWith = "Y"
}
if ($startsWith eq "Y")
{
print $str0." starts with ".$prefix."\n";
}
else
{
print $str0." does not start with ".$prefix."\n";
}
}
I'm almost ashamed to put this up here because I have no Idea what I'm doing yet. But I am trying to learn. I don't know how to do true false in perl thats why I have the $startsWith variable. you can fix that if you want. the part I need to fix is the line
if(str0 =~ /^($prefix)/)
I also need to find out how to refer to the first letter in str0...I think
A couple points without giving away the answer:
1) Arguments to functions are passed in a special variable called #_, which is what you are accessing when you say $_[0] and $_[1], but can be written much more concisely by assigned the argument list (#_) to your variables in list context
sub starts_with {
my ($str0, $prefix) = #_;
...
}
2) This statement: if($prefix =~ /^($str0)/) tests the exact opposite condition you are trying to prove. It says does the prefix start with the value of the variable $str0. What you really want to test is if $str0 starts with $prefix.
It might also be using to prefix your pattern with m flag, m/PATTERN which means match this pattern.
3) You don't have a return statement in your function, (As #M42 points out) the result of the last expression is returned; that expression being print will return true. You probably want to return true or false explicity.
See if you can use this to get started.
What I would do :
use Modern::Perl; # or use strict; use warnings; use feature qw/say/;
sub starts_with {
# better use #_, the default array instead of just elements of them
# ...like $_[0]
my ($str, $pref) = #_;
# very short expression, the pattern matching return a boolean.
# \Q\E is there to treat the prefix as-is (no metacharacters)
return $str =~ /^\Q$pref\E/;
}
# using our function
if (starts_with("foobar", "f")) {
say "TRUE";
}
else {
say "FALSE";
}
Golfing it a bit...
sub starts_with { $_[0] =~ /^\Q$_[1]/ }
Don't hand that version in though :-)
I want to test the performance of two different approaches, in perl, of checking that one string is contained entirely within another.
The first approach is to take a string convert it to an array and test character by character whilst the second approach simply evaluates a regular expression (which I believe has the same order as a linear search through all the characters but doesn't incur the cost of assigning memory for an array, and copying characters into it (though it might have other costs involved)).
My initial approach to doing this test was to just stick both procedures (see below) in a big for loop (0 to 999999) and then time how long it takes for the program to finish; and at first it looked as though a regex match was much faster (12.926s vs 0.318s); but I then considered the possibility that upon evaluating the regex once the following iterations are trivial because it is cached. To test this I instead put my for loop on the command line (making each iteration of the perl script looping through 0 to 0 "memory-less") and noticed that they are both similar (albeit with some wild divergence from the average at times). But I strongly suspect this might be a poor conclusion because the time taken to start the script probably dominates the execution time of the script.
Is there a way (especially for when I want to look at something less trivial), of turning off the caching (if that's what is happening of course) so that I can fairly run procedures within a for loop (so I can call the script only once)?
Or is it the case that there is nothing clever going on and that a regex search really is much quicker in this example!?
my $teststr = "testing testing";
my $teststr2 = "testing tasted";
my $match = 1;
#procedure one - character by character checking
for (my $i = 0; $i < 1; $i++)
{
my #chrArr = split //, $teststr;
my #chrArr2 = split //, $teststr2;
for (my $j = 0; $j < #chrArr2; $j++)
{
if($chrArr[$j] != $chrArr2[$j])
{
$match = 0;
break;
}
}
}
#procedure 2 - regex matching
for (my $i = 0; $i < 1; $i++)
{
if($teststr !~ m/$teststr2/)
{
$match = 0;
}
}
Why don't you use the Banchmark module. It should fit perfectly here.
use Benchmark qw( timethese cmpthese);
--
cmic
Regular expression matching/searching is linear. Compiling the pattern is expensive. If you change $teststr2 on every iteration, no caching will be possible. For example:
#procedure 2 - regex matching
for (my $i = 0; $i < 1; $i++)
{
if($teststr !~ m/${i}$teststr2/)
{
$match = 0;
}
}
For example I have a string:
MsgNam=WMS.WEATXT|VersionsNr=0|TrxId=475665|MndNr=0257|Werk=0000|WeaNr=0171581054|WepNr=|WeaTxtTyp=110|SpraNam=ru|WeaTxtNr=2|WeaTxtTxt=100 111|
and I want to catch this: |TrxId=475665|
after TrxId= it could be any numbers and any amount of them, so regex should catch as well:
|TrxId=111333| and |TrxId=0000011112222| and |TrxId=123|
TrxId=(\d+)
That would give a group (1) with the TrxId.
PS: Use global modifier.
The regex should look somewhat like this:
TrxId=[0-9]+
It will match TrxId= followed by at least one digit.
An example solution in Python:
In [107]: data = 'MsgNam=WMS.WEATXT|VersionsNr=0|TrxId=475665|MndNr=0257|Werk=0000|WeaNr=0171581054|WepNr=|WeaTxtTyp=110|SpraNam=ru|WeaTxtNr=2|WeaTxtTxt=100 111|'
In [108]: m = re.search(r'\|TrxId=(\d+)\|', data)
In [109]: m.group(0)
Out[109]: '|TrxId=475665|'
In [110]: m.group(1)
Out[110]: '475665'
/MsgNam\=.*?\|(TrxId\=\d+)\|.*/
for example in perl:
$a = "MsgNam=WMS.WEATXT|VersionsNr=0|TrxId=475665|MndNr=0257|Werk=0000|WeaNr=0171581054|WepNr=|WeaTxtTyp=110|SpraNam=ru|WeaTxtNr=2|WeaTxtTxt=100111|";
$a =~ /MsgNam\=.*?\|(TrxId\=\d+)\|.*/;
print $1;
will print TrxId=475665
You know what your delimiters look like, so you don't need a regex, you need to split. Here's an implementation in Perl.
use strict;
use warnings;
my $input = "MsgNam=WMS.WEATXT|VersionsNr=0|TrxId=475665|MndNr=0257|Werk=0000|WeaNr=0171581054|WepNr=|WeaTxtTyp=110|SpraNam=ru|WeaTxtNr=2|WeaTxtTxt=100 111|";
my #first_array = split(/\|/,$input); #splitting $input on "|"
#Now, since the last character of $input is "|", the last element
#of this array is undef (ie the Perl equivalent of null)
#So, filter that out.
#first_array = grep{defined}#first_array;
#Also filter out elements that do not have an equals sign appearing.
#first_array = grep{/=/}#first_array;
#Now, put these elements into an associative array:
my %assoc_array;
foreach(#first_array)
{
if(/^([^=]+)=(.+)$/)
{
$assoc_array{$1} = $2;
}
else
{
#Something weird may be happening...
#we may have an element starting with "=" for example.
#Do what you want: throw a warning, die, silently move on, etc.
}
}
if(exists $assoc_array{TrxId})
{
print "|TrxId=" . $assoc_array{TrxId} . "|\n";
}
else
{
print "Sorry, TrxId not found!\n";
}
The code above yields the expected output:
|TrxId=475665|
Now, obviously this is more complex than some of the other answers, but it's also a bit more robust in that it allows you to search for more keys as well.
This approach does have a potential issue if your keys appear more than once. In that case, it's easy enough to modify the code above to collect an array reference of values for each key.
I would like to supply my regular expression with a 'default' value, so if the thing I was looking for is not found, it will return the default value as if it had found it.
Is this possible to do using regex?
It sounds like you want some sort of regex syntax that says "if the regexp does not match any part of the given string pretend that it matched the following substring: 'foobar'". Such a feature does not exist in any regexp syntax I've seen.
You'll probably need to something like this:
matched_string = string.find_regex_match(regex);
if(matched_string == null) {
string = "default";
}
(This will of course need to be adjusted to the language you're using)
It's hard to answer this without a specific language, but in Perl at least, something like this works:
$string='hello';
$default = 1234;
($match) = ($string =~ m/(\d+)/ or $default);
print "$match\n";
1234
Not strictly part of the regex, but avoids the extra conditional block.
As far as I know, you can't do that with RegExp`s, at least with Perl Compatible Regular Expressions.
You can see by your self here.
Here's what I did in javascript...
function match(regx, str, dflt, index = 0) {
if (!str) return dflt
let x = str.match(regx)
return x ? x[index] || dflt : dflt
}