views:

635

answers:

9

Let's say I have a text file created using Data::Dumper, along the lines of:

my $x = [ { foo => 'bar', asdf => undef }, 0, -4, [ [] ] ];

I'd like to read that file back in and get $x back. I tried this:

my $vars;
{
  undef $/;
  $vars = <FILE>;
}

eval $vars;

But it didn't seem to work -- $x not only isn't defined, when I try to use it I get a warning that "Global symbol $x requires explicit package name."

What's the right way to do this? (And yes, I know it's ugly. It's a quick utility script, not e.g., a life-support system)

+11  A: 

Here's a thread that provides a couple different options - http://www.perlmonks.org/?node_id=92509

If you're just looking for data persistence the Storable module might be your best bet.

Rich Reuter
+4  A: 

As Rich says, you probably don't want to use Data::Dumper for persistence, but rather something like Storable.

However, to answer the question asked... IIRC, Data::Dumper doesn't declare your variables to be my, so are you doing that yourself somehow?

To be able to eval the data back in, the variable needs to not be my within the eval. If your text file contained this:

$x = [ { foo => 'bar', asdf => undef }, 0, -4, [ [] ] ];

Then this would work:

my $vars;
{
  undef $/;
  $vars = <FILE>;
}

my $x;    
eval $vars;
print $x;
Adam Bellaire
+1  A: 

Are you sure that file was created by Data::Dumper? There shouldn't be a my in there.

Some other options are Storable, YAML, or DBM::Deep. I go through some examples in the "Persistence" chapter of Mastering Perl.

Good luck, :)

brian d foy
+5  A: 

By default, Data::Dumper output cannot be parsed by eval, especially if the data structure being dumped is circular in some way. However, you can set

$Data::Dumper::Purity = 1;

or

$obj->Purity(1);

where obj is a Data::Dumper object. Either of these will cause Data::Dumper to produce output that can be parsed by eval.

See the Data::Dumper documenatation at CPAN for all the details.

Dale Hagglund
A: 

I think you want to put

our $x;

into your code before accessing x. That will satisfy the strict error checking.

That being said, I join the other voices in suggesting Storable.

Arkadiy
A: 

This works fine for me:

writing out:

open(my $C, qw{>}, $userdatafile) or croak "$userdatafile: $!";
use Data::Dumper;
print $C Data::Dumper->Dump([\%document], [qw(*document)]);
close($C) || croak "$userdatafile: $!";

reading in:

open(my $C, qw{<}, $userdatafile) or croak "$userdatafile: $!";
local $/ = $/; 
my $str = <$C>;
close($C) || croak "$userdatafile: $!";
eval { $str };
croak $@ if $@;
gms8994
+9  A: 

As others have already said, you'd probably be better off storing the data in a better serialisation format:

  • Storable - this is quick and easy, but fairly Perl-specific (but will satisfy your need for a quick solution in a relatively unimportant script easily)
  • YAML, using the YAML module, or YAML::Tiny, or YAML::Any as a wrapper to take advantage of whatever JSON module(s) are available on your system
  • JSON, using the JSON module, or JSON::XS for more speed (or JSON::Any as a wrapper to take advantage of whatever JSON module(s) are available on your system)
  • XML, using the XML-Simple module, or one of the other XML modules.

Personally, I think I'd aim for YAML or JSON... you can't get much easier than:

my $data = YAML::Any::LoadFile($filename);

David Precious
the JSON module now will use JSON::XS by default if it's installed on the system
plusplus
+3  A: 

If you want to stay with something easy and human-readable, simply use the Data::Dump module instead of Data::Dumper. Basically, it is Data::Dumper done right -- it produces valid Perl expressions ready for assignment, without creating all those weird $VAR1, $VAR2 etc. variables.

Then, if your code looks like:

my $x = [ { foo => 'bar', asdf => undef }, 0, -4, [ [] ] ];

Save it using:

use Data::Dump "pp";
open F, ">dump.txt";
print F pp($x);

This produces a file dump.txt that looks like (on my PC at least):

[{ asdf => undef, foo => "bar" }, 0, -4, [[]]]

Load it using:

open F, "dump.txt";
my $vars;
{ local $/ = undef; $vars = <F>; }
my $x = eval $vars;

Note that

  1. If you're bothering to put the assignment to $/ in its own block, you should use local to ensure it's value is actually restored at the end of the block; and
  2. The result of eval() needs to be assigned to $x.
j_random_hacker
A: 

This snippet is short and worked for me (I was reading in an array). It takes the filename from the first script argument.


# Load in the Dumper'ed library data structure and eval it
my $dsname = $ARGV[0];
my @lib = do "$dsname";
Marty