Sprintf in Perl doesn't display hexadecimal character properly - perl

I have a code like this.
$entry = &function(); //returns a number between 0 to 20
$var = sprintf("%#.4x", $entry);
if($var=~ /$hex/)
{
//block of statements
}
$hex will be within 0x0000 ..... 0x0014. Now, when function returns from 1 to 20, $var matches $hex. (Like 0x0001 .... 0x0014)
But when $entry is 0, $var becomes 0000. But I want it to be 0x0000. Currently, I am checking if that is 0000, I am changing it through a if loop. Please let me know if that is possible in sprintf itself.

According to the documentation for sprintf:
flags
# prefix non-zero hexadecimal with "0x" or "0X"
Note that it says non-zero, so only non-zero values will be prefixed by 0x.
A simple fix is to add the prefix manually:
sprintf "0x%04x", $entry;

The doc clearly mentions that 0x is appended only for non-zero numbers when # flag is used.This makes sense since zero is zero whether it is in Octal or Hexadecimal. Hence prefixing it with 0x doesn't make sense.
Best way to handle this would be:
if($var=~ /$hex/ or !$var)

Sounds like you are doing things backwards. Wouldn't the following make more sense?
if ($entry == hex($hex))
If you want to compare numbers, compare the numbers, not their text representation.

Related

Remove upfront zeros from floating point lower than 1 in Perl

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.

Perl Cryptology: Encrypting/Decrypting ASCII chracters with pack and unpack functions

I need help figuring out how these two subroutines work and what values or data structures they return. Here's a minimal representation of the code:
#!/usr/bin/perl
use strict; use warnings;
# an array of ASCII encrypted characters
my #quality = ("C~#p)eOA`/>*", "DCCec)ds~~", "*^&*"); # for instance
# input the quality
# the '#' character in front deferences the subroutine's returned array ref
my #q = #{unpack_qual_to_phred(#quality)};
print pack_phred_to_qual(\#q) . "\n";
sub unpack_qual_to_phred{
my ($qual)=#_;
my $upack_code='c' . length($qual);
my #q=unpack("$upack_code",$qual);
for(my $i=0;$i<#q;$i++){
$q[$i]-=64;
}
return(\#q);
}
sub pack_phred_to_qual{
my ($q_ref)=#_;
#q=#{$q_ref};
for(my $i=0;$i<#q;$i++){
$q[$i]+=64;
}
my $pack_code='c' . int(#q);
my $qual=pack("$pack_code",#q);
return ($qual);
}
1;
From my understanding, the unpack_qual_to_phread() subroutine apparently decrypts the ASCII character elements stored in #quality. The subroutine reads in an array containing elements of ASCII characters. Each element of the array is processed and apparently decrypted. The subroutine then returns an array ref containing elements of the decrypted array. I understand this much however I'm not really familiar with the Perl functions pack and unpack. Also I was unable to find any good examples of them online.
I think the pack_phred_to_qual subroutine converts the quality array ref back into ASCII characters and prints them.
thanks. any help or suggestions are greatly appreciated. Also if someone could provide a simple example of how Perl's pack and unpack functions work that would help too.
Calculating the length is needless. Those functions can be simplified to
sub unpack_qual_to_phred { [ map $_ - 64, unpack 'c*', $_[0] ] }
sub pack_phred_to_qual { pack 'c*', map $_ + 64, #{ $_[0] } }
In encryption terms, it's a crazy simple substitution cypher. It simply subtracts 64 from the character number of each character. It could have been written as
sub encrypt { map $_ - 64, #_ }
sub decrypt { map $_ + 64, #_ }
The pack/unpack doesn't factor in the encryption/decryption at all; it's just a way of iterating over each byte.
It is fairly simple, as packs go. Is is calling unpack("c12", "C~#p)eOA/>*)` which takes each letter in turn and finds the ascii value for that letter, and then subtracts 64 from the value (well, subtracting 64 is a post-processing step, nothing to do with pack). So letter "C" is ascii 67 and 67-64 is 3. Thus the first value out of that function is a 3. Next is "~" which is ascii 126. 126-64 is 62. Next is # which is ascii 35, and 35-64 is -29, etc.
The complete set of numbers being generated from your script is:
3,62,-29,48,-23,37,15,1,32,-17,-2,-22
The "encryption" step simply reverses this process. Adds 64 and then converts to a char.
This is not a full answer to your question, but did you read perlpacktut? Or the pack/unpack docs on perldoc? Those will probably go a long way to helping you understand.
EDIT:
Here's a simple way to think of it: say you have a 4-byte number stored in memory, 1234. If that's in a perl scalar, $num, then
pack('s*', $num)
would return
π♦
or whatever the actual internal storage value of "1234" is. So pack() treated the scalar value as a string, and turned it into the actual binary representation of the number (you see "pi-diamond" printed out, because that's the ASCII representation of that number). Conversely,
unpack('s*', "π♦")
would return the string "1234".
The unpack() part of your unpack_qual_to_phred() subroutine could be simplified to:
my #q = unpack("c12", "C~#p)e0A`/>*");
which would return a list of ASCII character pairs, each pair corresponding to a byte in the second argument.

Perl - Remove trailing zeroes without exponential value

I am trying to remove trailing zeroes from decimal numbers.
For eg: If the input number is 0.0002340000, I would like the output to be 0.000234
I am using sprintf("%g",$number), but that works for the most part, except sometimes it converts the number into an exponential value with E-. How can I have it only display as a full decimal number?
Numbers don't have trailing zeroes. Trailing zeroes can only occur once you represent the number in decimal, a string. So the first step is to convert the number to a string if it's not already.
my $s = sprintf("%.10f", $n);
(The solution is suppose to work with the OP's inputs, and his inputs appear to have 10 decimal places. If you want more digits to appear, use the number of decimal places you want to appear instead of 10. I thought this was obvious. If you want to be ridiculous like #asjo, use 324 decimal places for the doubles if you want to make sure not to lose any precision you didn't already lose.)
Then you can delete the trailing zeroes.
$s =~ s/0+\z// if $s =~ /\./;
$s =~ s/\.\z//;
or
$s =~ s/\..*?\K0+\z//;
$s =~ s/\.\z//;
or
$s =~ s/\.(?:|.*[^0]\K)0*\z//;
To avoid scientific notation for numbers use the format conversion %f instead of %g.
A lazy way could be simply: $number=~s/0+$// (substitute trailing zeroes by nothing).
The solution is easier than you might think.
Instead of using %g use %f and it will result in the behavior you are looking for. %f will always output your floating decimal in "fixed decimal notation".
What does the documentation say about %g vs %f?
As you may notice in the below table %g will result in either the same as %f or %e (when appropriate).
Ff you'd want to force the use of fixed decimal notation use the appropriate format identifier, which in this case is %f.
sprintf - perldoc.perl.org
%% a percent sign
%c a character with the given number
%s a string
%d a signed integer, in decimal
%u an unsigned integer, in decimal
%o an unsigned integer, in octal
%x an unsigned integer, in hexadecimal
%e a floating-point number, in scientific notation
%f a floating-point number, in fixed decimal notation
%g a floating-point number, in %e or %f notation
What about TIMTOWTDI; aren't we writing perl?
Yes, as always there are more than one ways of doing it.
If you'd just like to trim the trailing decimal-point zeros from a string you could use a regular expression such as the below.
$number = "123000.321000";
$number =~ s/(\.\d+?)0+$/$1/;
$number # is now "12300.321"
Remember that floating point values in perl doesn't have trailing decimals, unless you are dealing with a string. With that said; a string is not a number, even though it can explicitly and implicitly be converted to one.
The simplest way is probably to multiply by 1.
Original:
my $num = sprintf("%.10f", 0.000234000001234);
print($num);
#output
0.0002340000
With multiplying:
my $num = sprintf("%.10f", 0.000234000001234) * 1;
print($num);
#output
0.000234
The whole point of the %g format is to use a fixed point notation when it is reasonable and to use exponential notation when fixed point is not reasonable. So, you need to know the range of values you'll be dealing with.
Clearly, you could write a regular expression to post-process the string from sprintf(), removing the trailing zeroes:
my $str = sprintf("%g", $number);
$str =~ s/0+$//;
If you always want a fixed point number, use '%f', possibly with number of decimal places that you want; you might still need to remove trailing zeroes. If you always want exponential notation, use '%e'.
An easy way:
You could cheat a little. Add 1 to avoid the number breaking into scientific notation. Then manipulate the number as a string (thereby making perl convert it into a string).
$n++;
$n =~ s/^(\d+)(?=\.)/$1 - 1/e;
print $n;
A "proper" way:
For a more "proper" solution, counting the number of decimal places to use with %f would be optimal. It turned out getting the correct number of decimal points is trickier than one would think. Here's an attempt:
use strict;
use warnings;
use v5.10;
my $n = 0.000000234;
say "Original: $n";
my $l = getlen($n);
printf "New : %.${l}f\n", $n;
sub getlen {
my $num = shift;
my $dec = 0;
return 0 unless $num; # no 0 values allowed
return 0 if $num >= 1; # values above 1 don't need this computation
while ($num < 1) {
$num *= 10;
$dec++;
}
$num =~ s/\d+\.?//; # must have \.? to accommodate e.g. 0.01
$dec += length $num;
return $dec;
}
Output:
Original: 2.34e-007
New : 0.000000234
The value can have trailing zeroes only if it is a string.
You can add 0 to it. It will be coverted to a numerical value, not showing any trailing zeroes.

How can I generate non-repetitive random 4 bytes hex values in Perl?

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))

How do I get the length of a string in Perl?

What is the Perl equivalent of strlen()?
length($string)
perldoc -f length
length EXPR
length Returns the length in characters of the value of EXPR. If EXPR is
omitted, returns length of $_. Note that this cannot be used on an
entire array or hash to find out how many elements these have. For
that, use "scalar #array" and "scalar keys %hash" respectively.
Note the characters: if the EXPR is in Unicode, you will get the num-
ber of characters, not the number of bytes. To get the length in
bytes, use "do { use bytes; length(EXPR) }", see bytes.
Although 'length()' is the correct answer that should be used in any sane code, Abigail's length horror should be mentioned, if only for the sake of Perl lore.
Basically, the trick consists of using the return value of the catch-all transliteration operator:
print "foo" =~ y===c; # prints 3
y///c replaces all characters with themselves (thanks to the complement option 'c'), and returns the number of character replaced (so, effectively, the length of the string).
length($string)
The length() function:
$string ='String Name';
$size=length($string);
You shouldn't use this, since length($string) is simpler and more readable, but I came across some of these while looking through code and was confused, so in case anyone else does, these also get the length of a string:
my $length = map $_, $str =~ /(.)/gs;
my $length = () = $str =~ /(.)/gs;
my $length = split '', $str;
The first two work by using the global flag to match each character in the string, then using the returned list of matches in a scalar context to get the number of characters. The third works similarly by splitting on each character instead of regex-matching and using the resulting list in scalar context