Convert byte to sequence of bits (binary) in Perl - perl

I've been banging my head around this question for the past few hours; there's a lot of similar questions around here, but nothing quite the same, and none of the techniques I've seen seem to be working.
I have a sequence of bytes (integers) that I've generated from input in my program - each one represent a red, green or blue color value of a pixel in a BMP image. I essentially need to extract the bitstream representation of each byte; that is, the binary sequence of that byte.
I've been using lots of different variations of pack() and unpack(), but I'm not coming out with proper results.
For instance:
sub convertToBinary {
my $str = unpack("B32", pack("N", shift));
return $str;
}
I've also tried:
my $str = unpack("b8", shift);,
my $str = unpack("B8", shift);,
my $str = unpack("b*", shift);
And numerous other variations; none of them are seem to be working. I don't feel like it should be too hard to extract the bitpattern of a byte though.. just eight '1's or '0's, right?
What am I missing here?

I think you're looking for sprintf
sub convertToBinary {
return sprintf '%08b', shift;
}

Base on a comment, you actually want to check if the least significant bit of a byteis set.
The solution depends what you mean by byte.
If you have an 8-bit character:
if (ord("\xAC") & 0x01)
If you have an 8-bit number:
if (0xAC & 0x01)
Original answer:
It sounds like you want the binary representation of a byte. The solution depends what you mean by byte.
If you have an 8-bit character:
unpack('B8', "\xAC")
sprintf('%08b', ord("\xAC"))
sprintf('%08b', unpack('C', "\xAC"))
If you have an 8-bit number:
sprintf('%08b', 0xAC)
unpack('B8', chr(0xAC))
unpack('B8', pack('C', 0xAC))
All of the above produce the string 10101100.

Related

Sprintf in Perl doesn't display hexadecimal character properly

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.

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 converts to int wrong but only with specific number

the following perl code converts a float number to the wrong integer number
use strict;
my $zahl =297607.22000;
$zahl=$zahl * 100;
print "$zahl\n";
my $text=sprintf ("%017d",$zahl);
print $text;
The output of this is :
29760722
00000000029760721
The thing is, you can change the given number to other numbers and it works.
Any idea what is wrong here or does Perl simply do it wrong?
Thanks for your help!
This is related to a FAQ (Why am I getting long decimals). $zahl is not rounded properly, it is rounded down to the next lower integer.
22/100 is a periodic number in binary just like 1/3 is a periodic number in decimal. It would take infinite storage to store it exactly in a floating point number.
$ perl -e'$_="297607.22000"; $_*=100; printf "%.20f\n", $_'
29760721.99999999627470970154
int and sprintf %d truncate decimals, so you end up with 29760721. print and sprintf %f round, so you can get the desired result.
$ perl -e'$_="297607.22000"; $_*=100; printf "%017.0f\n", $_'
00000000029760722
When you are doing your floating point multiplication by 100 the result will be something like 29760721.9999999963. Then when you do the %d conversion to an integer this is truncated to 29760721.
Try sprintf('%.10f', $zahl) and you should be able to see this.
You have to be really careful with floating point numbers and treating them as fixed point. Due to various conversions that may take place in the builtins, there may be times where one integer conversion is not exactly the same as another. It appears that this happens many times with x.22 numbers:
use strict;
my $n = 0;
for (0 .. 10_000_000) {
my $float = 100 * "$_.22";
my $str = "$float";
my $int = int $float;
if ($str ne $int) {
$n++;
#say "$float, $str, $int";
}
}
say "n = $n";
which prints
n = 76269
on my system.
A careful look at the Perl source would be required to see where the exact conversion difference is.
I would recommend that if you are going to be working with fixed point numbers, to convert them all to integers (using a common conversion function, preferably looking at the source numbers as strings), and then work with them all under the use integer; pragma which will disable floating point numbers.

explain the following Perl Code?

Can anybody explain the following Perl code for me, please?
I think its in Perl and I have no clue about Perl programming. Please explain what the following code does?
$t = test(10);
sub test() {
my $str = unpack("B32", pack("N",shift));
$str2 = substr($str,16,length($str));
return $str2;
}
The pack, unpack and substr functions are documented here, here and here, respectively.
pack("N"...) packs a number into a four-byte network-order representation. unpack("B32"...) unpacks this packed number as a string of bits (zeros and ones). The substr call takes the second half of this bit string (from bit 16 onwards), which represents the lower 16 bits of the original 32-bit number.
Why it does it this way is a mystery to me. A simpler and faster solution is to deal with the lower 16 bits at the outset (note the lower case "n"):
sub test($) {
return unpack("B16", pack("n",shift));
}
shift
pops the first argument to the function from the list of arguments passed
pack("N", shift)
returns a 32bit network byte order representation of that value
my $str = unpack("B32", pack("N", shift));
stores a bitstring representation (32 bits worth) of said value (i.e. a string that looks like "00010011").
The substr is buggy and should be substr($str, 16); to get the last 16 characters of the above. (or substr($str, 16, 16);.)
In addition to Marcelo's answer, the shift function takes the #_ as its default argument. #_ contains the subroutine's arguments.
pack("N", shift) takes the argument of the function (return value of shift, which works on the arguments array by default) and makes it into an integer. The unpack("B32, part then makes it into string again, of 32 bits, so a string of 0's and 1's. The substr just takes the last 16 bit-characters, in this case.

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