Perl numeric variables - perl

Perl and I disagree as to whether a variable is a number or not. I'm wrong, of course, but why?
my $bytes = 0;
# . . .
$bytes = $bytes + length($text);
# . . .
my $MB = $bytes / 1048576;
my $string = sprintf("Downloaded: %.1f MB\n", MB);
gives Argument "MB" isn't numeric in sprintf at foo.pl line 200..
It is, in fact, numeric as can be seen when I use
my $string = "Downloaded: $MB MB\n";
which sets the string to Downloaded: 3.09680080413818 MB.
Edit: Ah, silly mistake, thanks for catching it.

You need the variable sigil:
my $bytes = 0;
# . . .
$bytes = $bytes + length($text);
# . . .
my $MB = $bytes / 1048576;
my $string = sprintf("Downloaded: %.1f MB\n", $MB); # <--- note the $MB at the end

Looks like you probably meant:
my $string = sprintf("Downloaded: %.1f MB\n", $MB);
other than that this should work.

Related

wrong result with s command in perl

Hi i want to find & replace some content of string
$source_folder = "D:\workdir\Devunit\11.1\latest";
$desitnation_folder = "D:\workdir\Devunit\10.1.3_Super\latest_Super";
$src_file = D:\workdir\Devunit\11.1\latest\src\tcbom\userver\buslogic\framework\bomitemfactory.cpp;
$dest_file = $src_file;
print " $dest_file \n";
$dest_file =~ s/$source_folder/$desitnation_folder/;
print " $dest_file \n";
both print results shows
D:\workdir\Devunit\11.1\latest\src\tcbom\userver\buslogic\framework\bomitemfactory.cpp
plz let me know where I am wrong
Use quotemeta if you don't want anything in your strings to be special, i.e. to have those interpreted literally. The following should work for you:
$source_folder = q(D:\workdir\Devunit\11.1\latest);
$desitnation_folder = q(D:\workdir\Devunit\10.1.3_Super\latest_Super);
$src_file = q(D:\workdir\Devunit\11.1\latest\src\tcbom\userver\buslogic\framework\bomitemfactory.cpp);
$dest_file = $src_file;
print " $dest_file \n";
$dest_file =~ s/\Q$source_folder\E/$desitnation_folder/;
print " $dest_file \n";
You might also want to refer to How do I match a regular expression that's in a variable?

What does dot-equals mean in Perl?

What does ".=" mean in Perl (dot-equals)? Example code below (in the while clause):
if( my $file = shift #ARGV ) {
$parser->parse( Source => {SystemId => $file} );
} else {
my $input = "";
while( <STDIN> ) { $input .= $_; }
$parser->parse( Source => {String => $input} );
}
exit;
Thanks for any insight.
The period . is the concatenation operator. The equal sign to the right means that this is an assignment operator, like in C.
For example:
$input .= $_;
Does the same as
$input = $input . $_;
However, there's also some perl magic in this, for example this removes the need to initialize a variable to avoid "uninitialized" warnings. Try the difference:
perl -we 'my $x; $x = $x + 1' # Use of uninitialized value in addition ...
perl -we 'my $x; $x += 1' # no warning
This means that the line in your code:
my $input = "";
Is quite redundant. Albeit some people might find it comforting.
For pretty much any binary operator X, $a X= $b is equivalent to $a = $a X $b. The dot . is a string concatenation operator; thus, $a .= $b means "stick $b at the end of $a".
In your code, you start with an empty $input, then repeatedly read a line and append it to $input until there's no lines left. You should end up with the entire file as the contents of $input, one line at a time.
It should be equivalent to the loopless
local $/;
$input = <STDIN>;
(define line separator as a non-defined character, then read until the "end of line" that never comes).
EDIT: Changed according to TLP's comment.
You have found the string concatenation operator.
Let's try it :
my $string = "foo";
$string .= "bar";
print $string;
foobar
This performs concatenation to the $input var. Whatever is coming in via STDIN is being assigned to $input.

Can someone translate this badly written Perl code for me?

I am needing to obtain the algorithm used in this little bit of Perl code, but I know nothing about Perl. Usually that's not a problem since I will research the language, but this regular expression stuff is way over my head!
Could anybody pseudo-code this? I just need to know what's going on so I can implement it in something else, preferably PHP or even C++, but I'll worry about that part. I just need to somehow decipher what this is doing:
$a = $ARGV[0];
$a =~ s/[^A-F0-9]+//simg;
#b = reverse split /(\S{2})/,$a;
$c = join "", #b;
$c .= "0000";
$d = hex($c) % 999999929;
print "$d\n";
What's poorly written about it? It could use a better var names, but I don't know if that's possible (since the intermediary steps don't appear to have any nameable quality), leaving only an improper use of split. The pseudo code is almost a word for word translation.
$a = $ARGV[0];
$a =~ s/[^A-F0-9]+//simg;
#b = reverse split /(\S{2})/,$a;
$c = join "", #b;
$c .= "0000";
$d = hex($c) % 999999929;
print "$d\n";
should be
$a = $ARGV[0]; # Get a hex str from cmd line E3:C9:D4
$a =~ s/[^A-F0-9]+//simg; # Remove any non-hex digits E3C9D4
#b = reverse $a =~ /(..)/sg; # Extract "bytes"; reverse D4, C9, E3
$c = join "", #b; # Join them. D4C9E3
$c .= "0000"; # Append two NULs D4C9E30000
$d = hex($c) % 999999929; # Convert from hex to number and modulus
print "$d\n"; # Print the result (in decimal).
Slightly clearer:
$a = $ARGV[0];
$a =~ s/[^0-9A-Fa-f]+//g;
$a = join '', reverse $a =~ /(..)/sg;
$a .= "0000";
$a = hex($a);
$a %= 999999929;
print "$a\n";
There might be a bug in these snippets. On a Perl with 32-bit ints, hex will overflow if the input has more than four hex digits. A Perl with 64-bit ints will handle 12 hex digits.
You seem to have taken the code from here. It's meant to take a MAC address as input, meaning the code requires 64-bit integers or Math::BigInt to work. There's no way around it since you want to modulus a 64-bit value.
Here's a concise way to do it that only works on Perls with 64-bit integers:
my $mac = $ARGV[0];
$mac =~ s/[^0-9A-Fa-f]+//g;
die length($mac) != 12;
# "123456789ABC" => 0xBC9A785634120000
my $hash = unpack('Q<', pack('H*', "0000$mac"));
$hash %= 999999929;
print "$hash\n";
For portability, you're better off integrating Math::BigInt into the earlier version.
It's looking for a bunch octets in hex concatenated together as the first argument of the program, and applying modulus.
So, if the program is invoked as:
$ myprog.pl A0B0
then the value in $c will be B0A00000. Therefore, the value of $d should be 0x396A6C8E.
It is a particularly bad piece of code written by someone who is scared of pack and unpack.
$a = $ARGV[0]; # assign first command line arg to $a
$a =~ s/[^A-F0-9]+//simg; # delete non-hex from $a
#b = reverse split /(\S{2})/,$a; # split $a by 2 non-whitespace (saving them too) to array #b and reverse it
$c = join "", #b; # join array #b to scalar $c
$c .= "0000"; # append 4 zeros to $c
$d = hex($c) % 999999929; # get modulo
print "$d\n"; # print it
$a = $ARGV[0]; #Read in the first argument on the command line
$a =~ s/[^A-F0-9]+//simg; #Substitute non hex numbers with nothing *
#b = reverse split /(\S{2})/,$a; #What is left in $a, split by 2 non-space characters
$c = join "", #b; # put the array b into $c
$c .= "0000";
$d = hex($c) % 999999929; #Convert $c to an integer and % with 999999929
print "$d\n";
simg = i: case insensitive; g: global; m: multi-line; s: single-line;
In short, we are stripping off the first hex number, then reversing the order of bytes (2 hex numbers at a time) and doing a modulo on the result.

Perl - How to change every $variable occurrence of ";" in a string

Very new here so be gentle. :)
Here is the jist of what I want to do:
I want to take a string that is made up of numbers separated by semi-colons (ex. 6;7;8;9;1;17;4;5;90) and replace every "X" number of semicolons with a "\n" instead. The "X" number will be defined by the user.
So if:
$string = "6;7;8;9;1;17;4;5;90";
$Nth_number_of_semicolons_to_replace = 3;
The output should be:
6;7;8\n9;1;17\n4;5;90
I've found lots on changing the Nth occurrence of something but I haven't been able to find anything on changing every Nth occurrence of something like I am trying to describe above.
Thanks for all your help!
use List::MoreUtils qw(natatime);
my $input_string = "6;7;8;9;1;17;4;5;90";
my $it = natatime 3, split(";", $input_string);
my $output_string;
while (my #vals = $it->()) {
$output_string .= join(";", #vals)."\n";
}
Here is a quick and dirty answer.
my $input_string = "6;7;8;9;1;17;4;5;90";
my $count = 0;
$input_string =~ s/;/++$count % 3 ? ";" : "\n"/eg;
Don't have time for a full answer now, but this should get you started.
$string = "6;7;8;9;1;17;4;5;90";
$Nth_number_of_semicolons_to_replace = 3;
my $regexp = '(' . ('\d+;' x ($Nth_number_of_semicolons_to_replace - 1)) . '\d+);';
$string =~ s{ $regexp ) ; }{$1\n}xsmg
sub split_x{
my($str,$num,$sep) = #_;
return unless defined $str;
$num ||= 1;
$sep = ';' unless defined $sep;
my #return;
my #tmp = split $sep, $str;
while( #tmp >= $num ){
push #return, join $sep, splice #tmp, 0, $num;
}
push #return, join $sep, #tmp if #tmp;
return #return;
}
print "$_\n" for split_x '6;7;8;9;1;17;4;5;90', 3
print join( ',', split_x( '6;7;8;9;1;17;4;5;90', 3 ) ), "\n";
my $string = "6;7;8;9;1;17;4;5;90";
my $Nth_number_of_semicolons_to_replace = 3;
my $num = $Nth_number_of_semicolons_to_replace - 1;
$string =~ s{ ( (?:[^;]+;){$num} [^;]+ ) ; }{$1\n}gx;
print $string;
prints:
6;7;8
9;1;17
4;5;90
The regex explained:
s{
( # start of capture group 1
(?:[^;]+;){$num} # any number of non ';' characters followed by a ';'
# repeated $num times
[^;]+ # any non ';' characters
) # end of capture group
; # the ';' to replace
}{$1\n}gx; # replace with capture group 1 followed by a new line
If you've got 5.10 or higher, this could do the trick:
#!/usr/bin/perl
use strict;
use warnings;
my $string = '1;2;3;4;5;6;7;8;9;0';
my $n = 3;
my $search = ';.*?' x ($n -1);
print "string before: [$string]\n";
$string =~ s/$search\K;/\n/g;
print "print string after: [$string]\n";
HTH,
Paul

How can I pad part of a string with spaces, in Perl?

Which version would you prefer?
#!/usr/bin/env perl
use warnings;
use strict;
use 5.010;
my $p = 7; # 33
my $prompt = ' : ';
my $key = 'very important text';
my $value = 'Hello, World!';
my $length = length $key . $prompt;
$p -= $length;
Option 1:
$key = $key . ' ' x $p . $prompt;
Option 2:
if ( $p > 0 ) {
$key = $key . ' ' x $p . $prompt;
}
else {
$key = $key . $prompt;
}
say "$key$value"
I would prefer
sprintf "%-7s : %s", $key, $value;
or
sprintf "%-*s : %s", $p, $key, $value;
instead of all this weird stuff.
From the sprintf documentation:
The flag characters
'-' The converted value is to be left adjusted on the field boundary. (The default is right justification.) The converted value is padded on the right with blanks, rather than on the left with blanks or zeros. A '-' overrides a 0 if both are given.
The field width
An optional decimal digit string (with nonzero first digit) specifying a minimum field width. If the converted value has fewer characters than the field width, it will be padded with spaces on the left (or right, if the left-adjustment flag has been given). Instead of a decimal digit string one may write '*' or '*m$' (for some decimal integer m) to specify that the field width is given in the next argument, or in the m-th argument, respectively, which must be of type int. A negative field width is taken as a '-' flag followed by a positive field width. In no case does a nonexistent or small field width cause truncation of a field; if the result of a conversion is wider than the field width, the field is expanded to contain the conversion result.
I don't like option 2 as it introduces an unnecessary special case.
I would refactor out the construction of the prompt suffix:
# Possible at top of program
my $suffix = ( ' ' x $p ) . $prompt;
# Later...
$key .= $suffix ;
Call me old-school, but I'd use printf() or sprintf():
printf "%-33s%s%s\n", $key, $prompt, $value;
That left justifies the string $key into 33 spaces, adds $prompt and $value and a newline. If I wanted to calculate the length for the first part dynamically:
printf "%-*s%s%s\n", $len, $key, $prompt, $value;
Since it is one line instead of the question's 4 (option 1) or 6 (option 2), it scores favourably on the succinctness scale.
I looks a little weird, but this works (until now):
#!/usr/bin/env perl
use warnings; use strict;
use 5.010;
use utf8;
use Term::Size;
my $columns = ( Term::Size::chars *STDOUT{IO} )[0];
binmode STDOUT, ':encoding(UTF-8)';
use Text::Wrap;
use Term::ANSIColor;
sub my_print {
my( $key, $value, $prompt, $color, $p ) = #_;
my $length = length $key.$prompt;
$p -= $length;
my $suff = ( ' ' x $p ) . $prompt;
$key .= $suff;
$length = length $key;
my $col = $columns - $length;
$Text::Wrap::columns = $col;
my #array = split /\n/, wrap ( '','', $value ) ;
$array[0] = colored( $key, $color ) . $array[0];
for my $idx ( 1..$#array ) {
$array[$idx] = ( ' ' x $length ) . $array[$idx];
}
say for #array;
}
my $prompt = ' : ';
my $color = 'magenta';
my $p = 30;
my $key = 'very important text';
my $value = 'text ' x 40;
my_print( $key, $value, $prompt, $color, $p );