How can I convert the binary string $x_bin="0001001100101" to its numeric value $x_num=613 in Perl?
My preferred way is:
$x_num = oct("0b" . $x_bin);
Quoting from man perlfunc:
oct EXPR
oct Interprets EXPR as an octal string and returns the
corresponding value. (If EXPR happens to start
off with "0x", interprets it as a hex string. If
EXPR starts off with "0b", it is interpreted as a
binary string. Leading whitespace is ignored in
all three cases.)
sub bin2dec {
return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}
As usual, there's is also an excellent CPAN module that should be mentioned here: Bit::Vector.
The transformation would look something like this:
use Bit::Vector;
my $v = Bit::Vector->new_Bin( 32, '0001001100101' );
print "hex: ", $v->to_Hex(), "\n";
print "dec: ", $v->to_Dec(), "\n";
The binary strings can be of almost any length and you can do other neat stuff like bit-shifting, etc.
Actually you can just stick '0b' on the front and it's treated as a binary number.
perl -le 'print 0b101'
5
But this only works for a bareword.
You can use the eval() method to work around the bare-word restriction:
eval "\$num=0b$str;";
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.
i have a code in perl $str =~ s/([^\w ])/'%'.unpack('H2', $1)/eg; i am not undestanding what value will be stored in $str
Assuming $str is encoded using UTF-8, and assuming the code you provided is followed by $str =~ s/ /+/g, the result is a url-encoded string safe for use in URLs.
Specifically, the line of code in question replaces every non-word except spaces with a three character sequence starting with % and followed by two hex digits representing the character number.
For example,
foo's ⇒ foo%27s
20% ⇒ 20%25
A better solution would be to use uri_escape (for strings encoded using UTF-8) or uri_escape_utf8 (for strings of Unicode Code Points aka decoded strings) from URI::Escape.
Provided line of code modifies $str value according substitute rule set s/([^\w ])/'%'.unpack('H2', $1)/eg.
How does it work:
[^\w] - look at $str for character not \w known as complement to \w
\w - represents range [A-za-z0-9_], punctuation chars and Unicode marks see perlre
([^\w]) capture found character, 'store' it in $1
regex modifier e evaluates '%'.unpack('H2',$1) as substitution string
unpack('H2',$1) - unpack $1 with template 'H2' (hex representation of byte associated with $1)
take '%' and concatenate it with unpacked result
use result from step 6 as replacement string
regex modifier g instructs to make this operation for all occurrences in the $str
Without knowing initial $str value before this operation, impossible to evaluate final result.
If initial value is known then you can evaluate result by visiting https://regex101.com/ website.
Nothing could speak louder than sample code demonstrating transformation
use feature 'say';
$msg = "Date: Mar 6 2020, Msg: soon Alex's birthday";
$msg =~ s/([^\w ])/'%'.unpack('H2', $1)/eg;
say $msg;
Output
Date%3a Mar 6 2020%2c Msg%3a soon Alex%27s birthday
Following code demonstrates how "Hello World\n" will look as hex representation (for Dada).
use feature 'say';
my $msg = "Hello World!\n";
print $msg;
my $a = unpack('H*',$msg);
say $a;
Output
Hello World!
48656c6c6f20576f726c64210a
You could start by trying it out and seeing if that gives you a hint.
$ perl -E'$str = "&*("; $str =~ s/([^\w ])/"%".unpack('H2', $1)/eg; say $str'
%26%2a%28
So, we have a substitution operator that looks like this:
s/PATTERN/REPLACEMENT/OPTIONS
Our pattern is ([^\w ]) which means "match every individual character that isn't a 'word character' or a space and capture that character in $1.
The replacement string is "%".unpack('H2', $1). Which means "the character '%' followed by the result of running unpack('H2', $1). unpack() here is being used to convert characters to the hexadecimal equivalent of their ASCII code. "H" means "convert to hex" and "2" means produce two hex digits".
The options are /e which means "run this code and use the output as the replacement string" and /g which means "do this for every match in the input string".
Putting that all together, you have code that:
Looks for non-word characters
Converts them to their hexadecimal escape code
Replaces them in the string
Using URI::Escape is probably a better approach.
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.
I am parsing an html documents, and there is a variable var key = 0xa9 for example, i do use my regex and all, but the 0xa9 i am getting is stored in a variable as a string, is there any cast function or anything to convert it to a number?
EDIT :
I am sure i didn't explain myself well, this is what i have been trying to do :
$t = $t . chr ( ord(substr($e, $i, 1)) ^ $var); BUT $var = "0xa9" or whatever, the thing is it is a string, so in the previous operation i do get an error Argument "0xc2" isn't numeric in bitwise xor (^) at, that is why i want the exact same value but not as a string, in order to work, $var needs to be like $var = 0xa9 for example.
Try
print hex '0xAf'; # prints '175'
From perldoc
hex
Interprets EXPR as a hex string and returns the corresponding value.
(To convert strings that might start with either 0 , 0x , or 0b, see oct.)
If EXPR is omitted, uses $_ .
Please try this:
print hex $string
Try adding 0x0 to $var. It should convert the second operand in XOR operation to numeric value.
$t = $t . chr ( ord(substr($e, $i, 1)) ^ ($var + 0x0));
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