Convert KB, MB, GB, TB into KB using PERL - perl

I have the size of disk value as below.
323.2T, 123.23G, 1.011T, 2.42M.
How to convert all these into KB in Perl

I would build a hash of multipliers for each factor and use it in a regex substitution
The following starts with a multiple of 1 for Kilobytes and increases it my a factor of 1024 == 210 for each subsequent factor. You can change 1024 to 1000 == 103 if that's what you prefer
The substitution simply looks for a sequence of digits and decimal points followed by one of the eligible factor letters, does the multiplication and replaces the letter with K
use strict;
use warnings 'all';
use feature 'say';
my %factors;
{
my $f = 1;
for my $c ( qw/ K M G T P E / ) {
$factors{$c} = $f;
$f *= 1024;
}
}
my $s = '323.2T, 123.23G, 1.011T, 2.42M';
$s =~ s/([\d.]+)([KMGTPE])/$1 * $factors{$2} . 'K'/eg;
say $s;
output
347033357516.8K, 129216020.48K, 1085552984.064K, 2478.08K

Related

How to print an array in a matrix format

I am creating a cypher program. I want to transcript the key (given in $ARGV[1]) to a matrix of numbers.
But, I have some troubles figuring out how to print the array as a matrix without getting warnings/errors.
use strict;
use warnings;
use POSIX;
my #characters = split //, $ARGV[1];
#characters = map {ord($_)} 0 .. $#characters;
my $col_nb = ceil(sqrt($#characters));
for my $i (1 .. ($col_nb**2 - $#characters - 1)) { push #characters , 0; }
foreach my $i (0 .. $col_nb - 1) {
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. $col_nb * ($i + 1)];
printf("\n");
}
I am triyng to get an output like this : (key = "abcd")
48 49
50 51
But, I get these errors on the output :
Redundant argument in printf at test.perl line 9.
48 49
Redundant argument in printf at test.perl line 9.
50 51
You are off by one. Your array slice contains 3 numbers, but you only want 2. Change:
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. $col_nb * ($i + 1)];
to:
printf "%.0f\t" x $col_nb, #characters[$col_nb * $i .. ($col_nb * ($i + 1) - 1)];
You can add use diagnostics; to get a more verbose warning message:
(W redundant) You called a function with more arguments than other
arguments you supplied indicated would be needed. Currently only
emitted when a printf-type format required fewer arguments than were
supplied, but might be used in the future for e.g. "pack" in perlfunc.
Only use printf when you are doing formatting. Change:
printf("\n");
to:
print "\n";
Check the edge cases and off-by-one errors.
Also, you want the square root of the number of characters, not the number - 1.
Moreover, you don't need to create another array to hold the numbers, you can map the characters to them on the fly when printing.
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw{ ceil };
my #characters = split //, $ARGV[1];
my $col_nb = ceil(sqrt #characters);
for my $i (0 .. $col_nb - 1) {
printf "%d\t" x $col_nb,
map defined ? ord : 0,
#characters[$col_nb * $i .. $col_nb * ($i + 1) - 1];
print "\n";
}

Right shift a binary no and get the shifted bits in a variable

I have a binary no say and I have a value in variable say value = 4.
I want to right shift the binary no by no of bits stored in "value" variable and then want to store the shifted bits in a variable and also want to save binary no obtained after right shift in another variable
Example:
binary_number = 110000001
value =4
then shifting no of bits in "value" to right (11000001 >> value)
Now I want to finally have two variables one containing the binary no after shift and one variable with shifted bits.
For above example the solution that I want is
right_shifted_binary = 11000
bits_shifted = 0001
I can not find a proper documentation for the problem as most of the problem are telling about arithmetic right shift.
Generate a bit mask based on $value and use the AND (&) operator:
#!/usr/bin/perl
use warnings;
use strict;
my $binary = 0b110000001;
my $value = 4;
# create mask with $value left-most bits 1
my $mask = ~(~0 << $value);
print "INPUT: ", unpack("B*", pack("N", $binary)), " ($binary)\n";
# right shift by $value bits
my $right_shifted_binary = $binary >> $value;
print "RIGHT: ", unpack("B*", pack("N", $right_shifted_binary)), " ($right_shifted_binary)\n";
# extract "remainder" of shift using mask
my $bits_shifted = $binary & $mask;
print "REMAINDER: ", unpack("B*", pack("N", $bits_shifted)), " ($bits_shifted)\n";
exit 0;
Test run:
$ perl dummy.pl
INPUT: 00000000000000000000000110000001 (385)
RIGHT: 00000000000000000000000000011000 (24)
REMAINDER: 00000000000000000000000000000001 (1)
# Proof
$ echo "24 * 16 + 1" | bc
385
If the binary number is given as string you can convert it to an integer first:
my $binary_string = "110000001";
my $binary = unpack("N", pack("B32", substr("0" x 32 . $binary_string, -32)));
But if it is already a string then the solution would be much simpler:
#!/usr/bin/perl
use warnings;
use strict;
my $binary_string = "110000001";
my $value = 4;
print "INPUT: $binary_string\n";
print "RIGHT: ", substr($binary_string, 0, -$value), "\n";
print "REMAINDER: ", substr($binary_string, -$value), "\n";
exit 0:
$ perl dummy.pl
INPUT: 110000001
RIGHT: 11000
REMAINDER: 0001

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 do bitwise operation in perl to get the count of longest sequence of 0s between two 1s

Given an integer I would like to print bit by bit in perl. For instance given a number 9, i would like to get
1
0
0
1
How do i achive this. Essentially what I am trying to do is, to get the number of longest 0s between two 1s. Meaning if the bitwise representation of a number is this
1000001001, I would like this perl function to return 5.
I would like to know whats the best way to code this in perl. Am totally new to perl.
With leading zeroes:
my #bits = reverse unpack '(a)*', unpack 'B*', pack 'J>', $int;
Without:
my #bits = reverse unpack '(a)*', sprintf '%b', $int;
Notes:
reverse is used to place the least significant bit in $bits[0].
unpack '(a)*' is used to split the string into individual bits.
Both work with signed and unsigned integers.
Both work with integers of the size (in bytes) given by perl -V:ivsize.
If you leave it as a string, you can take advantage of the regex engine to extract the sequences of zeroes.
use List::Util qw( max );
my $bin = sprintf '%b', $num;
my $longest = ( max map length, $bin =~ /1(0+)(?=1)/g ) || 0;
In C, you might do something like the following, but in Perl, it might be less efficient than the earlier solution:
my $longest = 0;
if ($num) {
# Cast to unsigned so that >> inserts zeroes even for neg nums.
$num = ~~$num;
# Skip zeros not between 1s.
$num >>= 1 while !($num & 1);
while (1) {
# Skip 1s.
$num >>= 1 while $num & 1;
last if !$num;
# Count 0s.
my $len = 0; ++$len, $num >>= 1 while !($num & 1);
$longest = $len if $longest < $len;
}
}

perl way of getting a value expressed with the memory unit

I'm searching a way to reduce the following piece of code to a single regexp statement:
if( $current_value =~ /(\d+)(MB)*/ ){
$current_value = $1 * 1024 * 1024;
}
elsif( $current_value =~ /(\d+)(GB)*/ ){
$current_value = $1 * 1024 * 1024 * 1024;
}
elsif( $current_value =~ /(\d+)(KB)*/ ){
$current_value = $1 * 1024;
}
The code performs an evaluation of the value that can be expressed as a single number (bytes), a number and KB (kilobytes), with megabytes (MB) and so on. Any idea on how to reduce the block code?
Number::Format
use warnings;
use strict;
use Number::Format qw(format_bytes);
print format_bytes(1024), "\n";
print format_bytes(2535116549), "\n";
__END__
1K
2.36G
You could set up a hash like this:
my %FACTORS = ( 'KB' => 1024, 'MB' => 1024**2, 'GB' => 1024**3 );
And then parse the text like this:
if ( $current_value =~ /(\d+)(KB|MB|GB)/ ) {
$current_value = $1 * $FACTORS{$2};
}
In your example the regex has a * which I'm not sure you intend, because * means "zero or more" and so (+\d)(MB)* would match 10 or 10MB or 10MBMB or 10MBMBMBMBMBMBMB.
Using benzado's modified code, here is a test you can run to see if it works.
We advise you to always put code like this in a reusable method, and write a small unit-test for it:
use Test::More;
plan tests => 4;
##
# Convert a string denoting '50MB' into an amount in bytes.
my %FACTORS = ( 'KB' => 1024, 'MB' => 1024*1024, 'GB' => 1024*1024*1024 );
sub string_to_bytes {
my $current_value = shift;
if ( $current_value =~ /(\d+)(KB|MB|GB)/ ) {
$current_value = $1 * $FACTORS{$2};
}
return $current_value;
}
my $tests = {
'50' => 50,
'52KB' => 52*1024,
'55MB' => 55*1024*1024,
'57GB' => 57*1024*1024*1024
};
foreach(keys %$tests) {
is( string_to_bytes($_),$tests->{$_},
"Testing if $_ becomes $tests->{$_}");
}
Running this gives:
$ perl testz.pl
1..4
ok 1 - Testing if 55MB becomes 57671680
ok 2 - Testing if 50 becomes 50
ok 3 - Testing if 52KB becomes 53248
ok 4 - Testing if 57GB becomes 61203283968
Now you can
Add more testcases (what happens with BIG numbers? What do you want to happen? What for undef, for strings, when kB is written with small k, when you encounter kibiB or kiB or Kb?)
Turn this into a module
Write documentation in POD
Upload the Module to CPAN
And voilá!
You can do it in one regexp, by putting code snippits inside the regexp to handle the three cases differently
my $r;
$current_value =~ s/
(\d+)(?:
Ki (?{ $r = $^N * 1024 })
| Mi (?{ $r = $^N * 1024 * 1024 })
| Gi (?{ $r = $^N * 1024 * 1024 * 1024 })
)/$r/xso;
There is a problem with using KB for 1024 bytes. Kilo as a prefix generally means 1000 of a thing not 1024.
The problem gets even worse with MB since it has meant 1000*1000, 1024*1024, and 1000*1024.
A 1.44 MB floppy actually holds 1.44 * 1000 * 1024.
The only real way out of this is to use the new KiB (Kibibyte) to mean 1024 bytes.
The way you implemented it also has the limitation that you can't use 8.4Gi to mean 8.4 * 1024 * 1024. To remove that limitation I used $RE{num}{real} from Regexp::Common instead of \d+.
Some of the other answers hardwire the match by writing out all of the possible matches. That can get very tedious, not to mention error prone. To get around that I used the keys of %multiplier to generate the regex. This means that if you add or remove elements from %multiplier you won't have to modify the regex by hand.
use strict;
use warnings;
use Regexp::Common;
my %multiplier;
my $multiplier_match;
{
# populate %multiplier
my %exponent = (
K => 1, # Kilo Kibi
M => 2, # Mega Mebi
G => 3, # Giga Gibi
T => 4, # Tera Tebi
P => 5, # Peta Pebi
E => 6, # Exa Exbi
Z => 7, # Zetta Zebi
Y => 8, # Yotta Yobi
);
while( my ($str,$exp) = each %exponent ){
#multiplier{ $str, "${str}B" } = (1000 ** $exp) x2; # K KB
#multiplier{ "${str}i", "${str}iB" } = (1024 ** $exp) x2; # Ki KiB
}
# %multiplier now holds 32 pairs (8*4)
# build $multiplier_match
local $" #" # fix broken highlighting
= '|';
my #keys = keys %multiplier;
$multiplier_match = qr(#keys);
}
sub remove_multiplier{
die unless #_ == 1;
local ($_) = #_;
# s/^($RE{num}{real})($multiplier_match)$/ $1 * $multiplier{$2} /e;
if( /^($RE{num}{real})($multiplier_match)$/ ){
return $1 * $multiplier{$2};
}
return $_;
}
If you absolutely need 1K to mean 1024 then you only need to change one line.
# #multiplier{ $str, "${str}B" } = (1000 ** $exp) x2; # K KB
#multiplier{ $str, "${str}B" } = (1024 ** $exp) x2; # K KB
Note that since I used $RE{num}{real} from Regexp::Common it will also work with 5.3e1Ki.