Handle command line arguments with different radices in Perl - perl

When used as literals in a Perl program, numbers are handled in the same way as in C: "0x" prefix means hexadecimal, "0" prefix means octal, and no prefix means decimal:
$ perl -E 'print 0x23 . "\n"'
35
$ perl -E 'print 023 . "\n"'
19
$ perl -E 'print 23 . "\n"'
23
I would like to pass command line arguments to Perl using the same notation. e.g. If I pass 23, I want to convert the string argument to a decimal value (23). If I pass 0x23, I want to convert to a hexadecimal value (35), and 023 would be converted to octal (19). Is there a built-in way to handle this? I am aware of hex() and oct(), but they interpret numbers with no prefix to be hex/oct respectively (not decimal). Following this convention, it seems that I want a dec() function, but I don't think that exists.

From http://answers.oreilly.com/topic/419-convert-binary-octal-and-hexidecimal-numbers-in-perl/:
print "Gimme an integer in decimal, binary, octal, or hex: ";
$num = <STDIN>;
chomp $num;
exit unless defined $num;
$num = oct($num) if $num =~ /^0/; # catches 077 0b10 0x20
printf "%d %#x %#o %#bn", ($num) x 4;

Related

perl - How to print utf8 code points for each byte

I'm trying to print the code points for all possible byte values.
My test file :
$ perl -e ' open($fh,">raw_bytes.dat");while($i++<256){ print $fh chr($i-1) } close($fh)'
$ ls -l raw_bytes.dat
-rw-rw-r--+ 1 uuuuu Domain Users 256 Mar 20 15:41 raw_bytes.dat
$
What should go into the below #---> part so that I print the code points of utf8 $x in hexadecimal?
perl -e ' use utf8; open($fh,"<raw_bytes.dat");binmode($fh);
while($rb=read($fh,$x,1)) { utf8::encode($x);
#--->
} '
I tried %02x using printf, but it didn't work. Also, I want the solution only using core modules.
Use unpack('H*'):
$ perl -e '$x="\x80"; utf8::encode($x); print unpack("H*", $x), "\n"'
c280
For your example file I get
$ perl -e 'open($fh, "<", "raw_bytes.dat"); binmode($fh);
while ($rb=read($fh,$x,1)) { utf8::encode($x);
print unpack("H*", $x), "\n";
}'
00
01
02
03
...
7f
c280
c281
c282
c283
...
c3bd
c3be
c3bf
Variants:
$ perl -e '$x="\x80"; utf8::encode($x);
print uc(unpack("H*", $x)), "\n"'
C280
$ perl -e '$x="\x80"; utf8::encode($x);
($r = uc(unpack("H*", $x))) =~ s/(..)/\\X\1/g;
print "$r\n"'
\XC2\X80
# a little bit pointless example, but assume that $x is a provided Perl scalar....
$ perl -e '$x="\N{U+0080}\N{U+0081}";
printf("U+%04x ", ord($_)) foreach(split(//, $x));
print "\n";'
U+0080 U+0081
Please remember the difference between
a scalar holding a raw string: split(//) returns octets, e.g. \x80
a scalar holding a properly encoded string: split(//) returns characters, e.g. \N{U+0080}
I tried %02x using printf, but it didn't work.
You can use
printf "%vX\n", $x;
According to perldoc sprintf:
vector flag
This flag tells Perl to interpret the supplied string as a vector of
integers, one for each character in the string. Perl applies the
format to each integer in turn, then joins the resulting strings with
a separator (a dot . by default). This can be useful for displaying
ordinal values of characters in arbitrary strings.

Perl - convert hexadecimal to binary and use it as string

I am new to Perl and I have difficulties using the different types.
I am trying to get an hexadecimal register, transform it to binary, use it a string and get substrings from the binary string.
I have done a few searches and what I tried is :
my $hex = 0xFA1F;
print "$hex\n";
result was "64031" . First surprise : can't I print the hex value in Perl and not just the decimal value ?
$hex = hex($hex);
print "$hex\n";
Result was 409649. Second surprise : I would expect the result to be also 64031 since "hex" converts hexadecimal to decimal.
my $bin = printf("%b", $hex);
It prints the binary value. Is there a way to transform the hex to bin without printing it ?
Thanks,
SLP
Decimal, binary, and hexadecimal are all text representations of a number (i.e. ways of writing a number). Computers can't deal with these as numbers.
my $num = 0xFA1F; stores the specified number (sixty-four thousand and thirty-one) into $num. It's stored in a format the hardware understands, but that's not very important. What's important is that it's stored as a number, not text.
When print is asked to print a number, it prints it out in decimal (or scientific notation if large/small enough). It has no idea how the number of created (from a hex constant? from addition? etc), so it can't determine how to output the number based on that.
To print an number as hex, you can use
my $hex = 'FA1F'; # $hex contains the hex representation of the number.
print $hex; # Prints the hex representation of the number.
or
my $num = 0xFA1F; # $num contains the number.
printf "%X", $num; # Prints the hex representation of the number.
You are assigning a integer value using hexadecimal format. print by default prints numbers in decimal format, so you are getting 64031.
You can verify this using the printf() by giving different formats.
$ perl -e ' my $num = 0xFA1F; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$ perl -e ' my $num = 64031; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$ perl -e ' my $num = 0b1111101000011111; printf("%d %X %b\n", ($num) x 3 ) '
64031 FA1F 1111101000011111
$
To get the binary format of 0xFA1F in string, you can use sprintf()
$ perl -e ' my $hex = 0xFA1F; my $bin=sprintf("%b",$hex) ; print "$bin\n" '
1111101000011111
$
lets take each bit of confusion in order
my $hex = 0xFA1F;
This stores a hex constant in $hex, but Perl doesn't have a hex data type so although you can write hex constants, and binary and octal constants for that matter, Perl converts them all to decimal. Note that there is a big difference between
my $hex = 0xFA1F;
and
my $hex = '0xFA1F';
The first stores a number into $hex, which when you print it out you get a decimal number, the second stores a string which when printed out will give 0xFAF1 but can be passed to the hex() function to be converted to decimal.
$hex = hex($hex);
The hex function converts a string as if it was a hex number and returns the decimal value and, as up to this point, $hex has only ever been used as a number Perl will first stringify $hex then pass the string to the hex() function to convert that value from hex to decimal.
So to the solution. You are almost there with printf(),there is a function called sprintf() which takes the same parameters as printf() but instead of printing the formatted value returns it as a string. So what you need is.
my $hex = 0xFA1F;
my $bin = sprintf("%b", $hex);
print $bin;
Technical note:
Yes I know that Perl stores all its numbers internally as binary, but lets not go there for this answer, OK?
If you're ok with using a distribution, I wrote Bit::Manip to make my prototyping a bit easier when dealing with registers (There's also a Pure Perl version available if you have problems compiling the XS code).
Not only can it fetch out bits from a number, it can toggle, clear, set etc:
use warnings;
use strict;
use Bit::Manip qw(:all);
my $register = 0xFA1F;
# fetch the bits from register using msb, lsb
my $msbyte = bit_get($register, 15, 8);
print "value: $msbyte\n";
print "bin: " . bit_bin($msbyte) . "\n";
# or simply:
# printf "bin: %b\n", $msbyte;
Output:
value: 250
bin: 11111010
Here's a blog post I wrote that shows how to use some of the software's functionality with an example datasheet register.

Perl converting a string into groups of 8 hex digits

I am trying to take a string, let's say "asdkljasdkjlaksjdla" and:
1. Print it in reverse order
2. convert it to hex
3. print in groups of 8 with leading 0x
So for example the string "asdkljasdkjlaksjdla" should print out like
0x616c646a
0x736b616c
0x6a6b6473
and so on.
So far I have this:
perl -e 'print unpack "H*", scalar reverse "asdkljasdkjlaksjdla"'
but I have not been able to figure out how to make the groups. Can you help?
You can do the hex conversion and splitting in one step with unpack "(H8)*":
print "0x$_\n" for unpack "(H8)*", scalar reverse "asdkljasdkjlaksjdla";
See perlpacktut for more information on this syntax.
(Ps. Strictly speaking, the scalar is unnecessary, since unpack already evaluates its second argument in scalar context. I'd rather have it there explicitly, though, so that the next person reading or editing this doesn't have to remember that detail. Feel free to remove it if you disagree.)
perl -E 'my $x= unpack "H*", scalar reverse "asdkljasdkjlaksjdla" ; say "0x$_" for ( $x =~ /(.{1,8})/g ) '
Needed another paren, and got it without the temp:
perl -E 'say "0x$_" for ( ( unpack "H*", scalar reverse "asdkljasdkjlaksjdla" ) =~ /(.{1,8})/g )'
If you have Perl 5.14.0 or later (needed for the /r modifier to s///):
perl -e 'print ((unpack "H*", reverse "asdkljasdkjlaksjdla") =~ s/.{2,8}/0x$&\n/gr)'
All the spaces are optional; all the parentheses are necessary.
Output:
0x616c646a
0x736b616c
0x6a6b6473
0x616a6c6b
0x647361

Perl -e field substitution

I am using perl -e to convert a hexadecimal number(523cc261) to a meaningful date:
perl -e 'my $t=localtime 0x523cc261; print $t . "\n"'
Fri Sep 20 21:47:13 2013
However i am not able to script it as above code needs the value to be provided on prompt.I tried substituting 523cc261 with a variable but it does not work:
b=523cc261
perl -e 'my $t=localtime 0x`echo b`; print $t . "\n"`
Backticks found where operator expected at -e line 1, near "0x`echo b`"
(Missing operator before `echo b`?)
syntax error at -e line 1, near "0x`echo b`"
My question is how to provide the decimal value(523cc261) via argument in a script.
Easiest way will be to pass the time to the Perl script as an argument. I've rewritten the script to be a little more concise, too:
% b=523cc261
% perl -E 'say scalar localtime hex $ARGV[0]' $b
Fri Sep 20 14:47:13 2013
You can use the ENV HASH :
$ b=523cc261 perl -le 'my $t = scalar localtime hex $ENV{"b"}; print $t;'
Another solution (a bit obfuscated, $b is a shell variable) :
$ b=523cc261 perl -le 'my $t = scalar localtime hex "'$b'"; print $t;'

Hexadecimal Computations in Perl

I have a string of ASCII characters. I convert this to hex string using the unpack function.
#! /usr/bin/perl
use strict;
use warnings;
my $str="hello";
my $value=unpack("H*",$str);
print $value,"\n";
**output:** 68656c6c6f
Now, lets say, I want to use this output as a string of hex bytes, read one byte at a time and perform some computation on it and store the output in another variable.
For instance,
#! /usr/bin/perl
use strict;
use warnings;
my $str="hello";
my $value=unpack("H*",$str);
my $num=0x12;
my $i=0;
while($i<length($value))
{
my $result.=(substr($value,$i,2)^$num);
$i+=2;
}
print $result,"\n";
**output:**
Argument "6c" isn't numeric in bitwise xor (^) at test.pl line 13.
Argument "6c" isn't numeric in bitwise xor (^) at test.pl line 13.
Argument "6f" isn't numeric in bitwise xor (^) at test.pl line 13.
8683202020
The output is incorrect and also there are several warnings.
If we take the first hex byte of the string, "hello" as an example:
68 xor 12 = 7A
However, the output shows it as 86. The output is incorrect and also I am not sure how
it got an output of 86.
What is the right way to do it?
If something is in hex, it is necessarily a string, since hex is a human-readable representation of a number. You don't want a string; you want a series of numbers, where each of those numbers is the numerical value of the char. You could use ord to get the number character by character, but unpack also provides the means:
my #bytes = unpack 'C*', $str;
Do the processing you want:
$_ ^= $num for #bytes;
And reconstitute the string:
$str = pack 'C*', #bytes;
The above three combined:
$str = pack 'C*', map $_ ^ $num, unpack 'C*', $str;
You can also do it as follows:
my $mask = chr($num) x length($str);
$str ^= $mask;