Is there any way/lib to transform a string to a hash ?
I have such string
{"hello"=>["world","perl"],"foo"=>"bar"}
and I would like to access the different values like if it was a hash
Thank you
Convert the string with Perl data structure to a string with JSON data structure by substitute => with : and decode it with JSON package.
#!/usr/bin/env perl
use warnings FATAL => 'all';
use strict;
use Data::Dumper;
use JSON qw(decode_json); # use JSON::XS for more performance
my $string = '{"hello"=>["world","perl"],"foo"=>"bar"}';
$string =~ s/"=>/":/g;
print Dumper(decode_json($string));
Output
$VAR1 = {
'hello' => [
'world',
'perl'
],
'foo' => 'bar'
};
Using eval():
#!/usr/bin/env perl
use strict;
use Data::Dumper;
my $string = qw( {"hello"=>["world","perl"],"foo"=>"bar"} );
print "String: $string\n";
my $hash = eval($string);
print "Hash: ", Dumper($hash), "\n";
Output
String: {"hello"=>["world","perl"],"foo"=>"bar"}
Hash: $VAR1 = {
'foo' => 'bar',
'hello' => [
'world',
'perl'
]
};
Using reval() and Safe if you are at all concerned about the input:
#!/usr/bin/env perl
use strict;
use Safe;
use Data::Dumper;
my $string = qw( {"hello"=>["world","perl"],"foo"=>"bar"} );
print "String: $string\n";
my $compartment = new Safe;
my $hash = $compartment->reval($string);
print $# ? "reval error: $#" : ("Hash: ", Dumper($hash)), "\n";
If you don't mind me plugging one of my own modules: Config::Perl uses PPI to parse strings like that, with no need for eval:
use warnings;
use strict;
use Data::Dumper; # Debug
use Config::Perl;
my $str = q( {"hello"=>["world","perl"],"foo"=>"bar"} );
my $data = Config::Perl->new->parse_or_die(\$str)->{_}[0];
print Dumper($data); # Debug
Output:
$VAR1 = {
'hello' => [
'world',
'perl'
],
'foo' => 'bar'
};
(The above code assumes that you've only got a single hash ref in your data, if you've got variations, you'll have to look at the whole data structure returned by parse_or_die.)
Related
I have a config file config.txt like
{sim}{time}{end}=63.1152e6;
{sim}{output}{times}=[2.592e6,31.5576e6,63.1152e6];
{sim}{fluid}{comps}=[ ['H2O','H_2O'], ['CO2','CO_2'],['NACL','NaCl'] ];
I would like to read this into a perl hash,
my %h=read_config('config.txt');
I have checked out module Config::Hash , but it does not offer the same input file format.
Can roll your own. Uses Data::Diver for traversing the hash, but could do that manually as well.
use strict;
use warnings;
use Data::Diver qw(DiveVal);
my %hash;
while (<DATA>) {
chomp;
my ($key, $val) = split /\s*=\s*/, $_, 2;
my #keys = $key =~ m/[^{}]+/g;
my $value = eval $val;
die "Error in line $., '$val': $#" if $#;
DiveVal(\%hash, #keys) = $value;
}
use Data::Dump;
dd \%hash;
__DATA__
{sim}{time}{end}=63.1152e6;
{sim}{output}{times}=[2.592e6,31.5576e6,63.1152e6];
{sim}{fluid}{comps}=[ ['H2O','H_2O'], ['CO2','CO_2'],['NACL','NaCl'] ];
Outputs:
{
sim => {
fluid => { comps => [["H2O", "H_2O"], ["CO2", "CO_2"], ["NACL", "NaCl"]] },
output => { times => [2592000, 31557600, 63115200] },
time => { end => 63115200 },
},
}
Would be better if you could come up with a way to not utilize eval, but not knowing your data, I can't accurately suggest an alternative.
Better Alternative, use a JSON or YAML
If you're picking the data format yourself, I'd advise using JSON or YAML for saving and loading your config data.
use strict;
use warnings;
use JSON;
my %config = (
sim => {
fluid => { comps => [["H2O", "H_2O"], ["CO2", "CO_2"], ["NACL", "NaCl"]] },
output => { times => [2592000, 31557600, 63115200] },
time => { end => 63115200 },
},
);
my $string = encode_json \%config;
## Save the string to a file, and then load below:
my $loaded_config = decode_json $string;
use Data::Dump;
dd $loaded_config;
I have a hash:
while( my( $key, $value ) = each %sorted_features ){
print "$key: $value\n";
}
but I cannot obtain the correct value for $value. It gives me:
intron: ARRAY(0x3430440)
source: ARRAY(0x34303b0)
exon: ARRAY(0x34303f8)
sig_peptide: ARRAY(0x33f0a48)
mat_peptide: ARRAY(0x3430008)
Why is it?
Your values are array references. You need to do something like
while( my( $key, $value ) = each %sorted_features ) {
print "$key: #$value\n";
}
In other words, dereference the reference. If you are unsure what your data looks like, a good idea is to use the Data::Dumper module:
use Data::Dumper;
print Dumper \%sorted_features;
You will see something like:
$VAR1 = {
'intron' => [
1,
2,
3
]
};
Where { denotes the start of a hash reference, and [ an array reference.
You can use also Data::Dumper::Pertidy which runs the output of Data::Dump through Perltidy.
#!/usr/bin/perl -w
use strict;
use Data::Dumper::Perltidy;
my $data = [{title=>'This is a test header'},{data_range=>
[0,0,3, 9]},{format => 'bold' }];
print Dumper $data;
Prints:
$VAR1 = [
{ 'title' => 'This is a test header' },
{ 'data_range' => [ 0, 0, 3, 9 ] },
{ 'format' => 'bold' }
];
Your hash values are array references. You need to write additional code to display the contents of these arrays, but if you are just debugging then it is probably simpler to use Data::Dumper like this
use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper \%sorted_features;
And, by the way, the name %sorted_features of your hash worries me. hashes are inherently unsorted, and the order that each retrieves the elements is essentially random.
Trying to use russian lettaz and console acts like a donkey, because does not react on use utf8/utf-8 or cp1251 directives.
What the encoding of the text marked by red colour I don't know.
Anybody knows how to solve that ? Code listing below:
#!/usr/bin/perl -w
use strict;
use warnings;
use Tie::IxHash;
tie my %hash, "Tie::IxHash";
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
print "В упорядоченной вставке список хеша такой:\n";
foreach my $qwerty (keys %hash){
print " $qwerty\n";
}
print "Кроме того, предметы обладают некоторыми свойствами:\n";
while((my($predmet, $opredelenie)) = each %hash){
print "$predmet $opredelenie","\n";
}
You need to specify STDOUT encoding. This script is utf-8 encoded:
use strict;
use warnings;
#use Tie::IxHash;
use utf8;
binmode STDOUT, ":encoding(cp866)";
my %hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная'
);
print "В упорядоченной вставке список хеша такой:\n";
foreach my $qwerty ( keys %hash ) {
print " $qwerty\n";
}
print "Кроме того, предметы обладают некоторыми свойствами:\n";
while ( ( my ( $predmet, $opredelenie ) ) = each %hash ) {
print "$predmet $opredelenie", "\n";
}
How do I increment a field in a Class::Struct object?
For now I am stuck with
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
$bar->counter($bar->counter()+1);
I wonder if there is something more expressive than the last line (the obvious $bar->counter++ results in Can't modify non-lvalue subroutine call).
EDIT: of course, I am not interested in $bar->[0]++ et al - what if I add a field before counter? I don't want to have to hunt my code for all such "bugs-in-waiting".
You can add an increment method to foo:
#!/usr/bin/env perl
package foo;
use strict; use warnings;
sub increment_counter {
my $self = shift;
my $val = $self->counter + 1;
$self->counter($val);
return $val;
}
package main;
use 5.012;
use strict;
use warnings;
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
$bar->increment_counter;
say $bar->counter;
__END__
Alternatively, try doing this :
use strict; use warnings;
use Class::Struct foo => [
counter => '$',
];
my $bar = foo->new(counter => 5);
print ++$bar->[0];
or using a SCALAR ref (no need to hard-code the "path" like the previous snippet) :
use strict; use warnings;
$\ = "\n";
use Class::Struct foo => [
counter => '*$',
];
my $bar = foo->new(counter => 5);
print ++${ $bar->counter };
I was wondering if you could shed some lights regarding the code I've been doing for a couple of days.
I've been trying to convert a Perl-parsed hash back to XML using the XMLout() and XMLin() method and it has been quite successful with this format.
#!/usr/bin/perl -w
use strict;
# use module
use IO::File;
use XML::Simple;
use XML::Dumper;
use Data::Dumper;
my $dump = new XML::Dumper;
my ( $data, $VAR1 );
Topology:$VAR1 = {
'device' => {
'FOC1047Z2SZ' => {
'ChassisID' => '2009-09',
'Error' => undef,
'Group' => {
'ID' => 'A1',
'Type' => 'Base'
},
'Model' => 'CATALYST',
'Name' => 'CISCO-SW1',
'Neighbor' => {},
'ProbedIP' => 'TEST',
'isDerived' => 0
}
},
'issues' => [
'TEST'
]
};
# create object
my $xml = new XML::Simple (NoAttr=>1,
RootName=>'data',
SuppressEmpty => 'true');
# convert Perl array ref into XML document
$data = $xml->XMLout($VAR1);
#reads an XML file
my $X_out = $xml->XMLin($data);
# access XML data
print Dumper($data);
print "STATUS: $X_out->{issues}\n";
print "CHASSIS ID: $X_out->{device}{ChassisID}\n";
print "GROUP ID: $X_out->{device}{Group}{ID}\n";
print "DEVICE NAME: $X_out->{device}{Name}\n";
print "DEVICE NAME: $X_out->{device}{name}\n";
print "ERROR: $X_out->{device}{error}\n";
I can access all the element in the XML with no problem.
But when I try to create a file that will house the parsed hash, problem arises because I can't seem to access all the XML elements. I guess, I wasn't able to unparse the file with the following code.
#!/usr/bin/perl -w
use strict;
#!/usr/bin/perl
# use module
use IO::File;
use XML::Simple;
use XML::Dumper;
use Data::Dumper;
my $dump = new XML::Dumper;
my ( $data, $VAR1, $line_Holder );
#this is the file that contains the parsed hash
my $saveOut = "C:/parsed_hash.txt";
my $result_Holder = IO::File->new($saveOut, 'r');
while ($line_Holder = $result_Holder->getline){
print $line_Holder;
}
# create object
my $xml = new XML::Simple (NoAttr=>1, RootName=>'data', SuppressEmpty => 'true');
# convert Perl array ref into XML document
$data = $xml->XMLout($line_Holder);
#reads an XML file
my $X_out = $xml->XMLin($data);
# access XML data
print Dumper($data);
print "STATUS: $X_out->{issues}\n";
print "CHASSIS ID: $X_out->{device}{ChassisID}\n";
print "GROUP ID: $X_out->{device}{Group}{ID}\n";
print "DEVICE NAME: $X_out->{device}{Name}\n";
print "DEVICE NAME: $X_out->{device}{name}\n";
print "ERROR: $X_out->{device}{error}\n";
Do you have any idea how I could access the $VAR1 inside the text file?
Regards,
newbee_me
$data = $xml->XMLout($line_Holder);
$line_Holder has only the last line of your file, not the whole file, and not the perl hashref that would result from evaling the file. Try something like this:
my $ref = do $saveOut;
The do function loads and evals a file for you. You may want to do it in separate steps, like:
use File::Slurp "read_file";
my $fileContents = read_file( $saveOut );
my $ref = eval( $fileContents );
You might want to look at the Data::Dump module as a replacement for Data::Dumper; its output is already ready to re-eval back.
Basically to load Dumper data you eval() it:
use strict;
use Data::Dumper;
my $x = {"a" => "b", "c"=>[1,2,3],};
my $q = Dumper($x);
$q =~ s{\A\$VAR\d+\s*=\s*}{};
my $w = eval $q;
print $w->{"a"}, "\n";
The regexp (s{\A\$VAR\d+\s*=\s*}{}) is used to remove $VAR1= from the beginning of string.
On the other hand - if you need a way to store complex data structure, and load it again, it's much better to use Storable module, and it's store() and retrieve() functions.
This has worked for me, for hashes of hashes. Perhaps won't work so well with structures which contain references other structures. But works well enough for simple structures, like arrays, hashes, or hashes of hashes.
open(DATA,">",$file);
print DATA Dumper(\%g_write_hash);
close(DATA);
my %g_read_hash = %{ do $file };
Please use dump module as a replacement for Data::Dumper
You can configure the variable name used in Data::Dumper's output with $Data::Dumper::Varname.
Example
use Data::Dumper
$Data::Dumper::Varname = "foo";
my $string = Dumper($object);
eval($string);
...will create the variable $foo, and should contain the same data as $object.
If your data structure is complicated and you have strange results, you may want to consider Storable's freeze() and thaw() methods.