How to separate the Hex 8 bits into two 4 bits - perl

I am using like this,
$a = "002c459f";
$b = $a%10000;
$c = int($a/10000);
print $b; #prints 0
print $c; #prints 2
I want
$b=459f;
$c=002c;
Can anyone suggest how will I get this?

If you had used warnings, you would have gotten a warning message indicating a problem.
Since your 8-bit input is already formatted as a simple hex string, you can just use substr:
use warnings;
use strict;
my $x = '002c459f';
my $y = substr $x, 0, 4;
my $z = substr $x, 4, 4;
print "z=$z, y=$y\n";
Output:
z=459f, y=002c
It is a good practice to also use strict. I changed your variable names since a and b are special variables in Perl.

You should always use use strict; use warnings;! It would have told you that 002c459f isn't a number. (It's the hex representation of a number.) As such, you can't use division before first converting it into a number. You also used the wrong divisor (10000 instead of 0x10000).
my $a_num = hex($a_hex);
my $b_num = $a_num % 0x10000; # More common: my $b_num = $a_num & 0xFFFF;
my $c_num = int( $a_num / 0x10000 ); # More common: my $c_num = $a_num >> 16
my $b_hex = sprintf("%04x", $b_num);
my $c_hex = sprintf("%04x", $c_num);
But if you have exactly eight characters, you can use the following instead:
my ($c, $b) = unpack('a4 a4', $a);
Note: You should avoid using $a and $b as it may interfere with sort and some subs.

Input data is a hex string, regular expression can be applied to split string by 4 characters into an array.
At this point you can use result as a strings, or you can use hex() to convert hex string representation into perl's internal digital representation.
use strict;
use warnings;
use feature 'say';
my $a = "002c459f"; # value is a string
my($b,$c) = $a =~ /([\da-f]{4})/gi;
say "0x$b 0x$c\tstrings"; # values are strings
$b = hex($b); # convert to digit
$c = hex($c); # convert to digit
printf "0x%04x 0x%04x\tdigits\n", $b, $c;
Output
0x002c 0x459f strings
0x002c 0x459f digits

Related

How to substring a string with several position with Perl?

I have several places where I want to cut my string in several parts.
For example:
$string= "AACCAAGTAA";
#cut_places= {0,4, 8 };
My $string should look like this: AACC AAGT AA;
How can I do that?
To populate an array, use round parentheses, not curly brackets (they're used for hash references).
One possible way is to use substr where the first argument is the position, so you can use the array elements. You just need to compute the length by subtracting the position from the following one; and to be able to compute the last length, you need the length of the whole string, too:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my $string = 'AACCAAGTAA';
my #cut_places = (0, 4, 8);
push #cut_places, length $string;
my #parts = map {
substr $string, $cut_places[$_], $cut_places[$_+1] - $cut_places[$_]
} 0 .. $#cut_places - 1;
say for #parts;
If the original array contained lengths instead of positions, the code would be much easier.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my $string = 'AACCAAGTAA';
my #lengths = (4, 4, 2); # 4, 4, 4 would work, too
my #parts = unpack join("", map "A$_", #lengths), $string;
say for #parts;
See unpack for details.
Here's a solution that starts by calculating the forward differences in the list of positions. The length of the string is first appended to the end of the list of it doesn't already span the full string
The differences are then used to build an unpack format string, which is used to build the required sequence of substrings.
I have written the functionality as a do block, which would be simple to convert to a subroutine if desired.
use strict;
use warnings 'all';
use feature 'say';
my $string = 'AACCAAGTAA';
my #cut_places = ( 0, 4, 8 );
my #parts = do {
my #places = #cut_places;
my $len = length $string;
push #places, $len unless $places[-1] >= $len;
my #w = map { $places[$_]-$places[$_-1] } 1 .. $#places;
my $patt = join ' ', map { "A$_" } #w;
unpack $patt, $string;
};
say "#parts";
output
AACC AAGT AA
Work out the lengths of needed parts first, then all methods are easier. Here regex is used
use warnings;
use strict;
use feature 'say';
my $string = 'AACCAAGTAA';
my #pos = (0, 4, 8);
my #lens = do {
my $prev = shift #pos;
"$prev", map { my $e = $_ - $prev; $prev = $_; $e } #pos;
};
my $patt = join '', map { '(.{'.$_.'})' } #lens;
my $re = qr/$patt/;
my #parts = grep { /./ } $string =~ /$re(.*)/g;
say for #parts;
The lengths #lens are computed by subtracting the successive positions, 2-1, 3-2 (etc). I use do merely so that the #prev variable, unneeded elsewhere, doesn't "pollute" the rest of the code.
The "$prev" is quoted so that it is evaluated first, before it changes in map.
The matches returned by regex are passed through grep to filter out empty string(s) due to the 0 position (or whenever successive positions are the same).
This works for position arrays of any lengths, as long as positions are consistent with a string.

Perl int to array

I have a number stored in a Perl variable and I want to 'pass/convert/store' its digits in the different positions of an array. An example for a better sight:
I have, let's say, this number stored:
$hello = 429384
And I need a new array with the digits stored in it, so:
$hello2[0] = 4
$hello2[1] = 2
$hello2[2] = 9
Etc..
I can probably make it with a couple of loops, but I want to know if there is an efficient and fast way to do it. Thx in advance!
my #hello = split //, $hello;
In Perl if you use number in a string operator, the conversion is done automatically
$hello = 429384;
#hello = split //, $hello;
print $hello[0];
Using only Regex and without using any inbuilt function:
#!/usr/bin/perl
use strict;
use warnings;
my $string=429384;
my #numbers = $string =~ /./g; # dot matches a single character at a time
#and returns it
print "#numbers \n";
this is significantly faster than the regexp way:
$string = '1234567890';
$_-=48 for #digits = unpack 'C*',$string;
benchmark:
use Time::HiRes;
$string = '1234567890';
$start_time = [Time::HiRes::gettimeofday()];
for (1.. 100000){
$_-=48 for #digits= unpack 'C*',$string;
}
$diff = Time::HiRes::tv_interval($start_time);
print "\n\n$diff\n";
$start_time = [Time::HiRes::gettimeofday()];
for (1.. 100000){
#digits = split //, $string;
}
$diff = Time::HiRes::tv_interval($start_time);
print "\n\n$diff\n";
output:
0.265814
0.314735

bitwise shift for a string holding a numeric hex. in perl

I have a string
$string = "0x0"
Now I want to basically use this string as a number and do a bitwise shift i.e my aim is to
$C = $string <<4 ;
But when I do this it says :
Argument "0x0" isn't numeric in left bitshift (<<)
Can someone please help to execute this ?
It's because "0x0" isn't numeric. It's a string. You would have to turn it into a numeric value.
use strict;
use warnings;
my $string = "0x0";
my $number = hex($string);
my $C = $number <<4 ;
print $C;
As mentioned, you have to convert it to a numeric value and use sprintf to turn back to a hexadecimal value.
sprintf ("0x%x" , hex($string) << 4);

How to print in decimal form rather than exponential form in perl

I have written a program in perl.My requirement is to print the only the decimal numbers, not exponential numbers. Could you please let me know how to implement this ?
My program is calculating the expression 1/2 power(n) , where n can take up integer numbers from 1 to 200 only. And only 100 lines should be printed.
Example:
N=1, print 0.5
N=2, print 0.25
My program looks like:
#!/usr/bin/perl
use strict;
use warnings;
my $exp;
my $num;
my $count_lines = 0;
while($exp = <>)
{
next if($exp =~ m/^$/);
if($exp > 0 and $exp <=200 and $count_lines < 100)
{
$num = 1/(2 ** $exp);
print $num,"\n";
$count_lines++;
}
}
Input values:
If N = 100 , then out is getting printed in exponential form. But, the requirement is it should get printed in decimal form.
A simple print will pick the "best" format to display the value, so it chooses scientific format for very large or very small numberss to avoid printing a long string of zeroes.
But you can use printf (the format specifiers are documented here) to format a number however you want.
0.5200 is a very small number, so you need around 80 decimal places
use strict;
use warnings;
while (my $exp = <>) {
next unless $exp =~ /\S/;
my $count_lines = 0;
if ($exp > 0 and $exp <= 200 and $count_lines < 100) {
my $num = 1 / (2 ** $exp);
printf "%.80f\n", $num;
$count_lines++;
}
}
output for 100
0.00000000000000000000000000000078886090522101181000000000000000000000000000000000
and for 200
0.00000000000000000000000000000000000000000000000000000000000062230152778611417000
If you would like to remove insignificant trailing zeroes then you can use sprintf to put the formatted number into a variable and then use s/// to delete trailing zeroes, like this
my $number = sprintf "%.80f", $num;
$number =~ s/0+$//;
print $number, "\n";
which gives
0.00000000000000000000000000000078886090522101181
and
0.00000000000000000000000000000000000000000000000000000000000062230152778611417
Note that the true value of the calculation has many more digits than this, and the accuracy of the result is limited by the size of the floating point values that your computer uses.
0.5 ^ 200 is too small for a double floating point number, you need to use Math::BigFloat, that will overload basic math operations and output operators such as print for you, for example:
#!/usr/bin/perl
use strict;
use warnings;
use Math::BigFloat;
my $x = Math::BigFloat->new('0.5');
my $y = Math::BigFloat->new('200');
print $x ** $y, "\n";
Or use bignum:
#!/usr/bin/perl
use strict;
use warnings;
use bignum;
print 0.5 ** 200, "\n";
Output:
$ perl t.pl
0.00000000000000000000000000000000000000000000000000000000000062230152778611417071440640537801242405902521687211671331011166147896988340353834411839448231257136169569665895551224821247160434722900390625
You can use printf or sprintf to specify the format of what you want to print out.
#!/usr/bin/perl
use strict;
use warnings;
my $num = 0.000000123;
printf("%.50", $num)
If you need something like Perl 5 formats, take a look at Perl6::Form (note, this is a Perl 5 module, it just implements the proposed Perl 6 version of formats).

How to truncate a string to a specific length in perl?

I am just unable to find "truncate a string to a specific length" in Perl.
Is there any built in way?
UPDATE:
input: $str = "abcd";
output (truncate for 3 characters): $str is abc
You want to use the substr() function.
$shortened = substr( $long, 0, 50 ); # 50 characters long, starting at the beginning.
For more, use perldoc
perldoc -f substr
In your case, it would be:
$str = 'abcd';
$short = substr( $str, 0, 3 );
For a string of arbitrary length, where truncate length can be longer than string length, I would opt for a substitution
$str =~ s/.{3}\K.*//s;
For shorter strings, the substitution will not match and the string will be unchanged. The convenient \K escape can be replaced with a lookbehind assertion, or a simple capture:
s/(?<=.{3}).*//s # lookbehind
s/(.{3}).*/$1/s # capture
It's probably useful to also mention that, instead of substr() or regular expressions, you could use printf or sprintf.
See perldoc -f sprintf :
For string conversions, specifying a precision truncates the string to
fit the specified width:
printf '<%.5s>', "truncated"; # prints "<trunc>"
printf '<%10.5s>', "truncated"; # prints "< trunc>"
As long as your original string is at least 3 characters long, you can use a call to substr as an lvalue.
my $str = "abcd";
substr($str, 3) = "";
print "$str\n"; # prints "abc"
The initial length of the string may need to be checked, as if it is shorter than 3 characters, the return value of this call to substr cannot be assigned to (see perldoc -f substr for more information) and attempting to do so will cause an error.
If I understand correctly, you need to do like php wordwrap() a string, so :
use Text::Format;
print Text::Format->new({columns => 50})->format($string);
If you just need the first N characters :
print substr $string, 0, 50;
Or you can use regexp to do the same.
#!/usr/bin/perl -w
use strict;
my $str = "abcd";
$str =~ /(\w{0,3})/;
print $1;
The most natural way is to use substr to extract the part you want:
$first_n = substr($string, 0, $n);
If you only want to modify the string and you are certain it is at least the desired length:
substr($string, $n) = '';
If you are not certain, you can do:
use List::Util "min";
substr($string, min($n, length($string))) = '';
or catch the exception:
eval { substr($string, $n) = '' };