How to convert string to floating point number inside a Perl hash? - perl

My motive is to convert the string number into floating point number while creating a hash.
I have placed my entire code and error below. Please help me to solve this issue.
Sample code
use strict;
use warnings;
use Data::Dumper;
my $price = 8.5;
my $g={};
$g->{'get'}=sprintf('%.02f',$price);
print Dumper($g);
Current output
$VAR1 = {
'get' => '8.50'
};
Expected output
$VAR1 = {
'get' => 8.50
};

Despite the single quotes around 8.50 in the Dumper output, Perl will still treat it as a numeric value when you go to use it:
use strict;
use warnings;
my $price = 8.5;
my $g={};
$g->{'get'}=sprintf('%.02f',$price);
my $x = 5;
printf "%.02f\n", $x + $g->{get};
Outputs:
13.50

use Scalar::Util 'looks_like_number';
.
.
print Dumper($g) =~ s/'(.*?)'/looks_like_number($1)?$1:"'$1'"/ger;
Which changes the output from Dumper before it's printed. It removes both 's of every single quoted string if it looks like a number according to Scalar::Util.

I suspect you're worrying unnecessarily here. Perl treats strings and numbers as largely interchangeable and will generally do the right thing with data of either type. The number of times when you should care if you have a string or a number is tiny.
In fact, even if you explicitly give Perl a number in code like yours, it will be displayed as a string:
$ perl -MData::Dumper -E'say Dumper { get => 8.5 }'
$VAR1 = {
'get' => '8.5'
};

Related

how to substitute actual value of variable in a file which is present in another file in perl

I have two file.Variables are declared in first file($one=1;) , in second file variable name is given ($one). I want to substitute this variable name with actual value and print the output.
File1.txt
variables are gieven here
$one=1;
$name="gorge";
$animal="cat";
File2.txt
This number is x=$one/or less then two
his name is $name
It is a $animal/ kind of animal.
Expected output
This number is x=1/or less then two
his name is gorge
It is a cat/ kind of animal.
I tried with this code:
open (data1,"</home/file1");
open (data2,"</home/file2");
while (<data1>){
while (<data2>){
print $_;
}
}
close data2;
close data1;
Thank You.
You need a templating system
One of the most popular ones is Template Toolkit
For example, with this template file
File2.template
This number is x=[% one %]/or less then two
his name is [% name %]
It is a [% animal %]/kind of animal.
And this Perl code
main.pl
use strict;
use warnings 'all';
use Template;
my $tt = Template->new;
my $vars = {
one => 1,
name => 'gorge',
animal => 'cat',
};
$tt->process('File2.template', $vars);
The result is this
output
This number is x=1/or less then two
his name is gorge
It is a cat/kind of animal.
I think you're fishing for something that is a horribly bad idea.
So I'll suggest a different approach, of building regular expressions to replace the text. In doing this though - the use of $one is going to be a bit confusing, because that means a scalar variable in perl, and this is "just" going to be a pattern match.
So if you can change that - you should:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %replace = ( 'one' => '1',
'name' => 'gorge',
'animal' => 'cat' );
my $search = join ( '|', keys %replace );
$search = qr/\$($search)/;
print Dumper \%replace;
print $search;
while ( <DATA> ) {
s/$search/$replace{$1}/g;
print;
}
__DATA__
This number is x=$one/or less then two
his name is $name
It is a $animal/ kind of animal.
You can build your replace patterns something like this:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my %replace = map { m/\$(\w+)=\"?([^;\"]+)/ } <DATA>;
print Dumper \%replace;
__DATA__
$one=1;
$name="gorge";
$animal="cat";
This gives you:
$VAR1 = {
'name' => 'gorge',
'one' => '1',
'animal' => 'cat'
};
If you're going to be any kind of Perl programmer, then you'll need to read the Perl FAQ.
In there, you'll find an answer to your question.
How can I expand variables in a text string?
If you read that answer, you'll end up with code very similar to what Sobrique gave you. However, in order to get to that code, you'll need to first pass the first paragraph in the answer which says:
If you can avoid it, don't, or if you can use a templating system, such as Text::Template or Template Toolkit, do that instead.
That's really good advice. You should follow it.

multidimensional array: argument isn't numeric in array element

OS: AIX
Shell: KSH
Following the accepted answer on this question I have created an multimensional array. Only, I get an error while trying to print the content of the array.
Error:
Argument "content of $pvid" isn't numeric in array element at...
The script:
#!/usr/bin/perl
use warnings;
use strict;
use Term::ANSIColor;
my #arrpvid = ();
print colored( sprintf("%-10s %9s %8s %8s %8s", 'PVID', 'AIX', 'VIO', 'VTD', 'VHOST'), 'green' ), "\n";
foreach my $pvid (`lspv | awk '{print \$2'}`) {
foreach my $hdaix (`lspv | awk '{print \$1'}`) {
chomp $pvid;
chomp $hdaix;
push #{ $arrpvid[$pvid] }, $hdaix;
}
}
print $arrpvid[0][0];
Some explanation:
Basically I want to print 5 variables of 5 different arrays next to each other. The code is written only for 2 arrays.
The content of $pvid:
00088da343b00d9b
00088da38100f93c
The content of $hdaix:
hdisk0
hdisk1
Quick Fix
Looks like you want to use a hash rather than an array, making your inner push
push #{ $arrpvid{$pvid} }, $hdaix;
Note the change from square brackets to curly braces immediately surrounding $pvid. This tells the compiler that you want %arrpvid and not #arrpvid, so be sure to tweak your my declaration as well.
At the end to print the contents of %arrpvid, use
foreach my $pvid (sort { hex $a <=> hex $b } keys %arrpvid) {
local $" = "]["; # handy trick due to mjd
print "$pvid: [#{$arrpvid{$pvid}}]\n";
}
The Data::Dumper module is quick and easy output tool.
use Data::Dumper;
$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper \%arrpvid;
More Details
You might be tempted to obtain the numeric value corresponding to each hexadecimal string in $pvid with hex as in
push #{ $arrpvid[hex $pvid] }, ...
but given the large example values in your question, #arrpvid would become enormous. Use a hash to create a sparse array instead.
Be sure that all the values of $pvid have the same padding. Otherwise, like values may not hash together appropriately. If you need to normalize, use code along the lines of
$pvid = sprintf "%016x", hex $pvid;
The problem lies in:
push #{ $arrpvid[$pvid] }, $hdaix;
The $pvid should be a numeric value like 0 or 5 and not i.e. 00088da343b00d9b

Using # and $ with the same variable name in Perl

I am declaring the same variable name with # and $:
#ask=(1..9);
$ask="insanity";
print ("Array #ask\n");
print ("Scalar $ask\n");
Without using use strict I am getting output correctly but when I am using use strict it gives me a compilation error.
Do these two variables refer to two different memory locations or is it the same variable?
You've got two variables:
#ask
$ask
You could have %ask (a hash) too if you wanted. Then you'd write:
print $ask, $ask[0], $ask{0};
to reference the scalar, the array and the hash.
Generally, you should avoid this treatment, but the variables are all quite distinct and Perl won't be confused.
The only reason use strict; is complaining is because you don't prefix your variables with my:
#!/usr/bin/env perl
use strict;
use warnings;
my #ask = (1..9);
my $ask = "insanity";
my %ask = ( 0 => 'infinity', infinity => 0 );
print "Array #ask\n";
print "Scalar $ask\n";
print "Hash $ask{0}\n";
with use strict; you need to declare your variables first before using it.
For example:
use strict;
my #ask=(1..9);
my $ask="insanity";
print ("Array #ask\n");
print ("Scalar $ask\n");
#ask and $ask are different variables — as is %ask — and it is not an error to do this. It is however poor style.
Because the sigil changes when you use them, such as when you use $ask[1] to get the second element of #ask, the code becomes harder to read and use strict will also not be able to tell if you've gotten confused. Thus it's a good idea to use names that differ in more than the sigil unless you know what you're doing. So you could use e.g. #asks and $ask.
The error you are getting with strict is not due to variable names. It is because you are not declaring the variables (using one of my, our, local, or state. Nor are you using the vars pragma.
Short answer: Stick a my in front of each variable, and you'll be strict-compliant.
For package variables, you can examine entries in the symbol table. $ask and #ask are separate entities:
#!/usr/bin/env perl
use Devel::Symdump;
use YAML;
#ask=(1..9);
$ask="insanity";
my $st = Devel::Symdump->new('main');
print Dump [ $st->$_ ] for qw(
scalars
arrays
);
Among other things, this code will output:
--
…
- main::ask
…
---
…
- main::ask
…
Being able to use the same name can help when, say, you have an array of fish and you are doing something with each fish in the array:
for my $fish (#fish) {
go($fish);
}
Normally, it is more expressive to use the plural form for arrays and hashes, the singular form for elements of an array, and something based on the singular form for keys in a hash:
#!/usr/bin/env perl
use strict;
use warnings;
my #ships = ('Titanic', 'Costa Concordia');
my %ships = (
'Titanic' => {
maiden_voyage => '10 April 1912',
capacity => 3_327,
},
'Costa Concordia' => {
maiden_voyage => '14 July 2006',
capacity => 4_880,
},
);
for my $ship (#ships) {
print "$ship\n";
}
while (my ($ship_name, $ship_details) = each %ships) {
print "$ship_name capacity: $ship_details->{capacity}\n";
}

How can I include a variable in a Perl printf expression?

How can I include a variable in a printf expression?
Here's my example:
printf "%${cols}s", $_;
Where $cols is the number of columns
and $_ is a string.
The statement results in an "Invalid conversion" warning.
The problem ended up being that I forgot to chomp the variable. Gah. Thanks everyone.
Your interpolated variable $cols looks like its supposed to be a number, say 10, so
"%${cols}s"
should interpolate and be equivalent to
"%10s"
which is a valid format string.
If however $cols was something other than a number or valid format string, you'd get the warning.
For example, if:
$cols = "w";
that would result in "%ws" as a format string - giving the error you quote:
Invalid conversion in printf: "%w"
Valid format information can be found here.
I figured out your specific problem. Your code is correct. However, I suppose $cols might be a number read from user input, say like this:
my $cols = <STDIN>;
This works, and in numeric context $cols will appear to be a number, but the problem is that $cols isn't appearing in numeric context here. It's in string context, which means that instead of expanding to "%5s", your format string expands to "%5\ns". The newline there is mucking up the format string.
Change the code where you read $cols to this:
chomp(my $cols = <STDIN>);
See the documentation on chomp, as you may want to use it for other input reading as well.
Always use * in your format specifier to unambiguously indicate variable width! This is similar to the advice to use printf "%s", $str rather than printf $str.
From the perlfunc documentation on sprintf:
(minimum) width
Arguments are usually formatted to be only as wide as required to display the given value. You can override the width by putting a number here, or get the width from the next argument (with *) or from a specified argument (with e.g. *2$):
printf '<%s>', "a"; # prints "<a>"
printf '<%6s>', "a"; # prints "< a>"
printf '<%*s>', 6, "a"; # prints "< a>"
printf '<%*2$s>', "a", 6; # prints "< a>"
printf '<%2s>', "long"; # prints "<long>" (does not truncate)
If a field width obtained through * is negative, it has the same effect as the - flag: left-justification.
For example:
#! /usr/bin/perl
use warnings;
use strict;
my $cols = 10;
$_ = "foo!";
printf "%*s\n", $cols, $_;
print "0123456789\n";
Output:
foo!
0123456789
With the warnings pragma enabled, you'll see warnings for non-numeric width arguments.
Your current method should work
perl -e'my $cols=500; $_="foo"; printf "%${cols}s\n\n", $_;'
The following seems to work for me:
#!/bin/perl5.8 -w
use strict;
my $cols = 5;
my $a = "3";
printf "%${cols}d\n", $a;
yields
28$ ./test.pl
3
29$
I cannot reproduce your problem. The following code works fine:
use strict;
use warnings;
my $cols=40;
while (<>) {
printf "%${cols}s\n", $_;
}
It prints any input line using at least 40 columns of width.

What's an easy way to print a multi-line string without variable substitution in Perl?

I have a Perl program that reads in a bunch of data, munges it, and then outputs several different file formats. I'd like to make Perl be one of those formats (in the form of a .pm package) and allow people to use the munged data within their own Perl scripts.
Printing out the data is easy using Data::Dump::pp.
I'd also like to print some helper functions to the resulting package.
What's an easy way to print a multi-line string without variable substitution?
I'd like to be able to do:
print <<EOL;
sub xyz {
my $var = shift;
}
EOL
But then I'd have to escape all of the $'s.
Is there a simple way to do this? Perhaps I can create an actual sub and have some magic pretty-printer print the contents? The printed code doesn't have to match the input or even be legible.
Enclose the name of the delimiter in single quotes and interpolation will not occur.
print <<'EOL';
sub xyz {
my $var = shift;
}
EOL
You could use a templating package like Template::Toolkit or Text::Template.
Or, you could roll your own primitive templating system that looks something like this:
my %vars = qw( foo 1 bar 2 );
Write_Code(\$vars);
sub Write_Code {
my $vars = shift;
my $code = <<'END';
sub baz {
my $foo = <%foo%>;
my $bar = <%bar%>;
return $foo + $bar;
}
END
while ( my ($key, $value) = each %$vars ) {
$code =~ s/<%$key%>/$value/g;
}
return $code;
}
This looks nice and simple, but there are various traps and tricks waiting for you if you DIY. Did you notice that I failed to use quotemeta on my key names in the substituion?
I recommend that you use a time-tested templating library, like the ones I mentioned above.
You can actually continue a string literal on the next line, like this:
my $mail = "Hello!
Blah blah.";
Personally, I find that more readable than heredocs (the <<<EOL thing mentioned elsewhere).
Double quote " interpolates variables, but you can use '. Note you'll need to escape any ' in your string for this to work.
Perl is actually quite rich in convenient things to make things more readable, e.g. other quote-operations. qq and q correspond to " and ' and you can use whatever delimiter makes sense:
my $greeting = qq/Hello there $name!
Nice to meet you/; # Interpolation
my $url = q|http://perlmonks.org/|; # No need to escape /
(note how the syntax coloring here didn't quite keep up)
Read perldoc perlop (find in page: "Quote and Quote-like Operators") for more information.
Use a data section to store the Perl code:
#!/usr/bin/perl
use strict;
use warnings;
print <DATA>;
#print munged data
__DATA__
package MungedData;
use strict;
use warnings;
sub foo {
print "foo\n";
}
Try writing your code as an actual perl subroutine, then using B::Deparse to get the source code at runtime.