I have a problem about perfect matching.I want to get the sum of positive and negative integers from a file .Also I want to get dates have same values in the file.
My File:
Hello -12, 3.4 and 32. Where did you
go on 01/01/2013 ? On 01/01/2013, we
went home. -4 plus 5 makes 1.
03/02/2013
Results I should be getting:
-16 //the sum of negative integers.
38 //the sum of positive integers.
2 //total number of unique dates :)
My code is:
$sum=0;
$summ=0;
while (<>) {
foreach ($_=~ /-\d+/g)
{
$sum+=$_;
}
foreach ($poz=~ /^\d+?$/g) {
$summ+=$poz;
}
foreach (/\d{2}\/\d{2}\/\d{4}/) {
$count++;
}
}
print "$sum\n";
print "$summ\n";
print "$count\n";
The output I am getting is:
-16
0
2
I can not get the value of the sum of positive numbers. Could you please help me?
First of all, always use use strict; use warnings;. It would have found your first error: The use of $poz without ever giving it a value. Twice!
A positive integer is a sequence
Not preceded by -.
Not preceded by a digit.
Not preceded by ..
Not preceded by /.
Consists of digits
Not followed by . plus digits. (Well, you might consider 4.0 an integer, but I doubt it.)
Not followed by a digit.
Not followed by /.
(?<![\-\d./])\d+(?![\d/])(?!\.\d)
A negative integer is a sequence
Consists of - followed by digits
Not followed by . plus digits. (Well, you might consider 4.0 an integer, but I doubt it.)
Not followed by a digit.
-\d+(?!\d)(?!\.\d)
So,
use strict;
use warnings;
my $sum_p = 0;
my $sum_n = 0;
my $dates = 0;
while (<>) {
$sum_p += $_ for /(?<![\-\d.\/])\d+(?![\d\/])(?!\.\d)/g;
$sum_n += $_ for /-\d+(?!\d)(?!\.\d)/g;
++$dates while /\d{2}\/\d{2}\/\d{4}/g;
}
print "$sum_p\n";
print "$sum_n\n";
print "$dates\n";
Related
I would like to normalize the variable from ie. 00000000.1, to 0.1 using Perl
my $number = 000000.1;
$number =\~ s/^0+(\.\d+)/0$1/;
Is there any other solution to normalize floats lower than 1 by removing upfront zeros than using regex?
When I try to put those kind of numbers into an example function below
test(00000000.1, 0000000.025);
sub test {
my ($a, $b) = #_;
print $a, "\n";
print $b, "\n";
print $a + $b, "\n";
}
I get
01
021
22
which is not what is expected.
A number with leading zeros is interpreted as octal, e.g. 000000.1 is 01. I presume you have a string as input, e.g. my $number = "000000.1". With this your regex is:
my $number = "000000.1";
$number =~ s/^0+(?=0\.\d+)//;
print $number;
Output:
0.1
Explanation of regex:
^0+ -- 1+ 0 digits
(?=0\.\d+) -- positive lookahead for 0. followed by digits
Learn more about regex: https://twiki.org/cgi-bin/view/Codev/TWikiPresentation2018x10x14Regex
Simplest way, force it to be treated as a number and it will drop the leading zeros since they are meaningless for decimal numbers
my $str = '000.1';
...
my $num = 0 + $str;
An example,† to run from the command-line:
perl -wE'$n = shift; $n = 0 + $n; say $n' 000.1
Prints 0.1
Another, more "proper" way is to format that string ('000.1' and such) using sprintf. Then you do need to make a choice about precision, but that is often a good idea anyway
my $num = sprintf "%f", $str; # default precision
Or, if you know how many decimal places you want to keep
my $num = sprintf "%.3f", $str;
† The example in the question is really invalid. An unquoted string of digits which starts with a zero (077, rather than '077') would be treated as an octal number except that the decimal point (in 000.1) renders that moot as octals can't be fractional; so, Perl being Perl, it is tortured into a number somehow, but possibly yielding unintended values.
I am not sure how one could get an actual input like that. If 000.1 is read from a file or from the command-line or from STDIN ... it will be a string, an equivalent of assigning '000.1'
See Scalar value constructors in perldata, and for far more detail, perlnumber.
As others have noted, in Perl, leading zeros produce octal numbers; 10 is just a decimal number ten but 010 is equal to decimal eight. So yeah, the numbers should be in quotes for the problem to make any sense.
But the other answers don’t explain why the printed results look funny. Contrary to Peter Thoeny’s comment and zdim’s answer, there is nothing ‘invalid’ about the numbers. True, octals can’t be floating point, but Perl does not strip the . to turn 0000000.025 into 025. What happens is this:
Perl reads the run of zeros and recognises it as an octal number.
Perl reads the dot and parses it as the concatenation operator.
Perl reads 025 and again recognises it as an octal number.
Perl coerces the operands to strings, i.e. the decimal value of the numbers in string form; 0000000 is, of course, '0' and 025 is '21'.
Perl concatenates the two strings and returns the result, i.e. '021'.
And without error.
(As an exercise, you can check something like 010.025 which, for the same reason, turns into '821'.)
This is why $a and $b are each printed with a leading zero. Also note that, to evaluate $a + $b, Perl coerces the strings to numbers, but since leading zeros in strings do not produce octals, '01' + '021' is the same as '1' + '21', returning 22.
Is there some easy way (without sprintf and, of course, printf) to get a list of (001, 002, ... 100) in Perl?
In bash it was something like seq -w 1 100. What about Perl?
you mean like this?
for ('001'..'100') {
print "$_\n";
}
.. in a range, returns a list of values counting by ones, from the left value to the right value.
For more details about how to use range, please refer to:
Perldoc range operator and this link
Printf was created for problems like this. Using it will help you get the answers you want faster.
foreach my $number ( 1 .. 100 ) {
printf "%03d\n", $number;
}
The % is the "begin a format sequence"
The 0 is "leading zeros"
The 3 is "three digits minimum"
The d is "treat the parameter as digits (integer)"
I have a tab delimited file with several columns (9 columns) that looks like this:
1:21468 1 21468 2.8628817609765984 0.09640845515631684 0.05034710996552612 1.0 0.012377712911711025 54.0
However in column 5 I sometimes have scientific numbers like:
8.159959468796783E-4
8.465114165595303E-4
8.703354859736187E-5
9.05132870067004E-4
I need to have all numbers in column 5 in decimal notation. From the example above:
0.0008159959468796783
0.0008465114165595303
0.00008703354859736187
0.000905132870067004
And I need to change these numbers without changing the rest of the numbers in column 5 or the rest of the file.
I know there is a similar post in Convert scientific notation to decimal in multiple fields. But in this case there was a if statement not related to the type of number present in the field, and it was for all numbers in that column. So, I'm having trouble transforming the information in there to my specific case. Can someone help me figuring this out?
Thank you!
The easyiest (and fastest) way to convert a scientific notation number in perl, to a regular notation number:
my $num = '0.12345678E5';
$num *= 1;
print "$num\n";
As Jim already proposed, one way to do this is to simply treat the number as a string and do the translation yourself. This way you're able to fully maintain your significant digits.
The following demonstrates a function for doing just that. It takes in a number that's potentially in scientific notation, and it returns the decimal representation. Works with both positive and negative exponents:
use warnings;
use strict;
while (<DATA>) {
my ($num, $expected) = split;
my $dec = sn_to_dec($num);
print $dec . ' - ' . ($dec eq $expected ? 'good' : 'bad') . "\n";
}
sub sn_to_dec {
my $num = shift;
if ($num =~ /^([+-]?)(\d*)(\.?)(\d*)[Ee]([-+]?\d+)$/) {
my ($sign, $int, $period, $dec, $exp) = ($1, $2, $3, $4, $5);
if ($exp < 0) {
my $len = 1 - $exp;
$int = ('0' x ($len - length $int)) . $int if $len > length $int;
substr $int, $exp, 0, '.';
return $sign.$int.$dec;
} elsif ($exp > 0) {
$dec .= '0' x ($exp - length $dec) if $exp > length $dec;
substr $dec, $exp, 0, '.' if $exp < length $dec;
return $sign.$int.$dec;
} else {
return $sign.$int.$period.$dec;
}
}
return $num;
}
__DATA__
8.159959468796783E-4 0.0008159959468796783
8.465114165595303E-4 0.0008465114165595303
8.703354859736187E-5 0.00008703354859736187
9.05132870067004E-4 0.000905132870067004
9.05132870067004E+4 90513.2870067004
9.05132870067004E+16 90513287006700400
9.05132870067004E+0 9.05132870067004
If you do this the simple way, by parsing as floating point and then using printf to force it to print as a decimal, you may end up with slightly different results because you're at the upper limit of precision available in double-precision format.
What you should do is split each line into fields, then examine field 5 with something like this.
($u,$d,$exp) = $field[5] =~ /(\d)\.(\d+)[Ee]([-+]\d+)/
If field[5] is in scientific notation this will give you
$u the digit before the decimal
$d the digits after the decimal
$exp the exponent
(if it's not you'll get back undefined values and can just skip the reformatting step)
Using that information you can reassemble the digits with the correct number of leading zeros and decimal point. If the exponent is positive you have to reassemble the digits but then insert the decimal point in the right place.
Once you've reformatted the value the way you want, reassemble the entire line (using, say, join) and write it out.
It's the same question as this one, but using Perl!
I would like to iterate over a value with just one leading zero.
The equivalent in shell would be:
for i in $(seq -w 01 99) ; do echo $i ; done
Since the leading zero is significant, presumably you want to use these as strings, not numbers. In that case, there is a different solution that does not involve sprintf:
for my $i ("00" .. "99") {
print "$i\n";
}
Try something like this:
foreach (1 .. 99) {
$s = sprintf("%02d",$_);
print "$s\n";
}
The .. is called the Range Operator and can do different things depending on its context. We're using it here in a list context so it counts up by ones from the left value to the right value. So here's a simpler example of it being used; this code:
#list = 1 .. 10;
print "#list";
has this output:
1 2 3 4 5 6 7 8 9 10
The sprintf function allows us to format output. The format string %02d is broken down as follows:
% - start of the format string
0 - use leading zeroes
2 - at least two characters wide
d - format value as a signed integer.
So %02d is what turns 2 into 02.
printf("%02d\n",$_) foreach (1..20)
print foreach ("001" .. "099")
foreach $i (1..99) {printf "%02d\n", $i;}
I would consider to use sprinft to format $i according to your requirements. E.g. printf '<%06s>', 12; prints <000012>.
Check Perl doc about sprinft in case you are unsure.
Well, if we're golfing, why not:
say for "01".."99"`
(assuming you're using 5.10 and have done a use 5.010 at the top of your program, of course.)
And if you do it straight from the shell, it'd be:
perl -E "say for '01'..'99'"
I want to generate random hex values and those values should not be repetitive
and it should be of 4 bytes (ie: 0x00000000 to 0xffffffff) and the display output
should contain leading zeros.
For example: if I get the value 1 it should not represented as 0x1 but 0x00000001.
I want a minimum of 100 random values. Please tell me: how can I do that in Perl?
To get a random number in the range 0 .. (2<<32)-1:
my $rand = int(rand(0x100000000));
To print it in hex with leading zeroes:
printf "%08x", $rand;
Do please note this from the Perl man page:
Note: If your rand function consistently returns numbers that
are too large or too small, then your version of Perl was probably compiled with the wrong number of RANDBITS
If that's a concern, do this instead:
printf "%04x%04x", int(rand(0x10000)), int(rand(0x10000));
Note, also, that this does nothing to prevent repetition, although to be honest the chance of a repeating 32 bit number in a 100 number sequence is pretty small.
If it's absolutely essential that you don't repeat, do something like this:
my (%a); # create a hash table for remembering values
foreach (0 .. 99) {
my $r;
do {
$r = int(rand(0x100000000));
} until (!exists($a{$r})); # loop until the value is not found
printf "%08x\n", $r; # print the value
$a{$r}++; # remember that we saw it!
}
For what it's worth, this algorithm shouldn't be used if the range of possible values is less than (or even near to) the number of values required. That's because the random number generator loop will just repeatedly pull out numbers that were already seen.
However in this case where the possible range is so high (2^32) and the number of values wanted so low it'll work perfectly. Indeed with a range this high it's about the only practical algorithm.
perl -e 'printf "%08X\n", int rand 0xFFFFFFFF for 1 .. 100'
Alnitak explained it, but here's a much simpler implementation. I'm not sure how everyone starting reaching for do {} while since that's a really odd choice:
my $max = 0xFFFF_FFFF;
my( %Seen, #numbers );
foreach ( 1 .. 100 )
{
my $rand = int rand( $max + 1 );
redo if $Seen{$rand}++;
push #numbers, $rand;
}
print join "\n", map { sprintf "0x%08x", $_ } #numbers;
Also, as Alnitak pointed out, if you are generating a lot of numbers, that redo might cycle many, many times.
These will only be pseudorandom numbers, but you're not really asking for real random number anyway. That would involve possible repetition. :)
use LWP::Simple "get";
use List::MoreUtils "uniq";
print for uniq map { s/\t//, "0x$_" } split /^/, LWP::Simple::get('http://www.random.org/integers/?num=220&min=0&max=65535&col=2&base=16&format=plain&rnd=date.2009-12-14');
Adjust the url (see the form on http://www.random.org/integers/?mode=advanced) to not always return the same list. There is a minuscule chance of not returning at least 100 results.
Note that this answer is intentionally "poor" as a comment on the poor question. It's not a single question, it's a bunch all wrapped up together, all of which I'd bet have existing answers already (how do I generate a random number in range x, how do I format a number as a hex string with 0x and 0-padding, how do I add only unique values into a list, etc.). It's like asking "How do I write a webserver in Perl?" Without guessing what part the questioner really wants an answer to, you either have to write a tome for a response, or say something like:
perl -MIO::All -e 'io(":80")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
To get a random integer:
int(rand(0x10000000))
To format it as 8 hexadecimal digits:
printf "%08x", int(rand(0x10000000))