📄 dumper.pm
字号:
#
# Data/Dumper.pm
#
# convert perl data structures into perl syntax suitable for both printing
# and eval
#
# Documentation at the __END__
#
package Data::Dumper;
$VERSION = $VERSION = '2.09';
#$| = 1;
require 5.004;
require Exporter;
require DynaLoader;
require overload;
use Carp;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw(Dumper);
@EXPORT_OK = qw(DumperX);
bootstrap Data::Dumper;
# module vars and their defaults
$Indent = 2 unless defined $Indent;
$Purity = 0 unless defined $Purity;
$Pad = "" unless defined $Pad;
$Varname = "VAR" unless defined $Varname;
$Useqq = 0 unless defined $Useqq;
$Terse = 0 unless defined $Terse;
$Freezer = "" unless defined $Freezer;
$Toaster = "" unless defined $Toaster;
$Deepcopy = 0 unless defined $Deepcopy;
$Quotekeys = 1 unless defined $Quotekeys;
$Bless = "bless" unless defined $Bless;
#$Expdepth = 0 unless defined $Expdepth;
#$Maxdepth = 0 unless defined $Maxdepth;
#
# expects an arrayref of values to be dumped.
# can optionally pass an arrayref of names for the values.
# names must have leading $ sign stripped. begin the name with *
# to cause output of arrays and hashes rather than refs.
#
sub new {
my($c, $v, $n) = @_;
croak "Usage: PACKAGE->new(ARRAYREF, [ARRAYREF])"
unless (defined($v) && (ref($v) eq 'ARRAY'));
$n = [] unless (defined($n) && (ref($v) eq 'ARRAY'));
my($s) = {
level => 0, # current recursive depth
indent => $Indent, # various styles of indenting
pad => $Pad, # all lines prefixed by this string
xpad => "", # padding-per-level
apad => "", # added padding for hash keys n such
sep => "", # list separator
seen => {}, # local (nested) refs (id => [name, val])
todump => $v, # values to dump []
names => $n, # optional names for values []
varname => $Varname, # prefix to use for tagging nameless ones
purity => $Purity, # degree to which output is evalable
useqq => $Useqq, # use "" for strings (backslashitis ensues)
terse => $Terse, # avoid name output (where feasible)
freezer => $Freezer, # name of Freezer method for objects
toaster => $Toaster, # name of method to revive objects
deepcopy => $Deepcopy, # dont cross-ref, except to stop recursion
quotekeys => $Quotekeys, # quote hash keys
'bless' => $Bless, # keyword to use for "bless"
# expdepth => $Expdepth, # cutoff depth for explicit dumping
# maxdepth => $Maxdepth, # depth beyond which we give up
};
if ($Indent > 0) {
$s->{xpad} = " ";
$s->{sep} = "\n";
}
return bless($s, $c);
}
#
# add-to or query the table of already seen references
#
sub Seen {
my($s, $g) = @_;
if (defined($g) && (ref($g) eq 'HASH')) {
my($k, $v, $id);
while (($k, $v) = each %$g) {
if (defined $v and ref $v) {
($id) = (overload::StrVal($v) =~ /\((.*)\)$/);
if ($k =~ /^[*](.*)$/) {
$k = (ref $v eq 'ARRAY') ? ( "\\\@" . $1 ) :
(ref $v eq 'HASH') ? ( "\\\%" . $1 ) :
(ref $v eq 'CODE') ? ( "\\\&" . $1 ) :
( "\$" . $1 ) ;
}
elsif ($k !~ /^\$/) {
$k = "\$" . $k;
}
$s->{seen}{$id} = [$k, $v];
}
else {
carp "Only refs supported, ignoring non-ref item \$$k";
}
}
return $s;
}
else {
return map { @$_ } values %{$s->{seen}};
}
}
#
# set or query the values to be dumped
#
sub Values {
my($s, $v) = @_;
if (defined($v) && (ref($v) eq 'ARRAY')) {
$s->{todump} = [@$v]; # make a copy
return $s;
}
else {
return @{$s->{todump}};
}
}
#
# set or query the names of the values to be dumped
#
sub Names {
my($s, $n) = @_;
if (defined($n) && (ref($n) eq 'ARRAY')) {
$s->{names} = [@$n]; # make a copy
return $s;
}
else {
return @{$s->{names}};
}
}
sub DESTROY {}
#
# dump the refs in the current dumper object.
# expects same args as new() if called via package name.
#
sub Dump {
my($s) = shift;
my(@out, $val, $name);
my($i) = 0;
local(@post);
$s = $s->new(@_) unless ref $s;
for $val (@{$s->{todump}}) {
my $out = "";
@post = ();
$name = $s->{names}[$i++];
if (defined $name) {
if ($name =~ /^[*](.*)$/) {
if (defined $val) {
$name = (ref $val eq 'ARRAY') ? ( "\@" . $1 ) :
(ref $val eq 'HASH') ? ( "\%" . $1 ) :
(ref $val eq 'CODE') ? ( "\*" . $1 ) :
( "\$" . $1 ) ;
}
else {
$name = "\$" . $1;
}
}
elsif ($name !~ /^\$/) {
$name = "\$" . $name;
}
}
else {
$name = "\$" . $s->{varname} . $i;
}
my $valstr;
{
local($s->{apad}) = $s->{apad};
$s->{apad} .= ' ' x (length($name) + 3) if $s->{indent} >= 2;
$valstr = $s->_dump($val, $name);
}
$valstr = "$name = " . $valstr . ';' if @post or !$s->{terse};
$out .= $s->{pad} . $valstr . $s->{sep};
$out .= $s->{pad} . join(';' . $s->{sep} . $s->{pad}, @post)
. ';' . $s->{sep} if @post;
push @out, $out;
}
return wantarray ? @out : join('', @out);
}
#
# twist, toil and turn;
# and recurse, of course.
#
sub _dump {
my($s, $val, $name) = @_;
my($sname);
my($out, $realpack, $realtype, $type, $ipad, $id, $blesspad);
return "undef" unless defined $val;
$type = ref $val;
$out = "";
if ($type) {
# prep it, if it looks like an object
if ($type =~ /[a-z_:]/) {
my $freezer = $s->{freezer};
# UNIVERSAL::can should be used here, when we can require 5.004
if ($freezer) {
eval { $val->$freezer() };
carp "WARNING(Freezer method call failed): $@" if $@;
}
}
($realpack, $realtype, $id) =
(overload::StrVal($val) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/);
# keep a tab on it so that we dont fall into recursive pit
if (exists $s->{seen}{$id}) {
# if ($s->{expdepth} < $s->{level}) {
if ($s->{purity} and $s->{level} > 0) {
$out = ($realtype eq 'HASH') ? '{}' :
($realtype eq 'ARRAY') ? '[]' :
"''" ;
push @post, $name . " = " . $s->{seen}{$id}[0];
}
else {
$out = $s->{seen}{$id}[0];
if ($name =~ /^([\@\%])/) {
my $start = $1;
if ($out =~ /^\\$start/) {
$out = substr($out, 1);
}
else {
$out = $start . '{' . $out . '}';
}
}
}
return $out;
# }
}
else {
# store our name
$s->{seen}{$id} = [ (($name =~ /^[@%]/) ? ('\\' . $name ) :
($realtype eq 'CODE' and
$name =~ /^[*](.*)$/) ? ('\\&' . $1 ) :
$name ),
$val ];
}
$s->{level}++;
$ipad = $s->{xpad} x $s->{level};
if ($realpack) { # we have a blessed ref
$out = $s->{'bless'} . '( ';
$blesspad = $s->{apad};
$s->{apad} .= ' ' if ($s->{indent} >= 2);
}
if ($realtype eq 'SCALAR') {
if ($realpack) {
$out .= 'do{\\(my $o = ' . $s->_dump($$val, "") . ')}';
}
else {
$out .= '\\' . $s->_dump($$val, "");
}
}
elsif ($realtype eq 'GLOB') {
$out .= '\\' . $s->_dump($$val, "");
}
elsif ($realtype eq 'ARRAY') {
my($v, $pad, $mname);
my($i) = 0;
$out .= ($name =~ /^\@/) ? '(' : '[';
$pad = $s->{sep} . $s->{pad} . $s->{apad};
($name =~ /^\@(.*)$/) ? ($mname = "\$" . $1) :
($name =~ /[]}]$/) ? ($mname = $name) : ($mname = $name . '->');
$mname .= '->' if $mname =~ /^\*.+\{[A-Z]+\}$/;
for $v (@$val) {
$sname = $mname . '[' . $i . ']';
$out .= $pad . $ipad . '#' . $i if $s->{indent} >= 3;
$out .= $pad . $ipad . $s->_dump($v, $sname);
$out .= "," if $i++ < $#$val;
}
$out .= $pad . ($s->{xpad} x ($s->{level} - 1)) if $i;
$out .= ($name =~ /^\@/) ? ')' : ']';
}
elsif ($realtype eq 'HASH') {
my($k, $v, $pad, $lpad, $mname);
$out .= ($name =~ /^\%/) ? '(' : '{';
$pad = $s->{sep} . $s->{pad} . $s->{apad};
$lpad = $s->{apad};
($name =~ /^\%(.*)$/) ? ($mname = "\$" . $1) :
($name =~ /[]}]$/) ? ($mname = $name) : ($mname = $name . '->');
$mname .= '->' if $mname =~ /^\*.+\{[A-Z]+\}$/;
while (($k, $v) = each %$val) {
my $nk = $s->_dump($k, "");
$nk = $1 if !$s->{quotekeys} and $nk =~ /^[\"\']([A-Za-z_]\w*)[\"\']$/;
$sname = $mname . '{' . $nk . '}';
$out .= $pad . $ipad . $nk . " => ";
# temporarily alter apad
$s->{apad} .= (" " x (length($nk) + 4)) if $s->{indent} >= 2;
$out .= $s->_dump($val->{$k}, $sname) . ",";
$s->{apad} = $lpad if $s->{indent} >= 2;
}
if (substr($out, -1) eq ',') {
chop $out;
$out .= $pad . ($s->{xpad} x ($s->{level} - 1));
}
$out .= ($name =~ /^\%/) ? ')' : '}';
}
elsif ($realtype eq 'CODE') {
$out .= '"DUMMY"';
$out = 'sub { ' . $out . ' }';
carp "Encountered CODE ref, using dummy placeholder" if $s->{purity};
}
else {
croak "Can\'t handle $realtype type.";
}
if ($realpack) { # we have a blessed ref
$out .= ', \'' . $realpack . '\'' . ' )';
$out .= '->' . $s->{toaster} . '()' if $s->{toaster} ne '';
$s->{apad} = $blesspad;
}
$s->{level}--;
}
else { # simple scalar
my $ref = \$_[1];
# first, catalog the scalar
if ($name ne '') {
($id) = ("$ref" =~ /\(([^\(]*)\)$/);
if (exists $s->{seen}{$id}) {
$out = $s->{seen}{$id}[0];
return $out;
}
else {
$s->{seen}{$id} = ["\\$name", $val];
}
}
if (ref($ref) eq 'GLOB' or "$ref" =~ /=GLOB\([^()]+\)$/) { # glob
my $name = substr($val, 1);
if ($name =~ /^[A-Za-z_][\w:]*$/) {
$name =~ s/^main::/::/;
$sname = $name;
}
else {
$sname = $s->_dump($name, "");
$sname = '{' . $sname . '}';
}
if ($s->{purity}) {
my $k;
local ($s->{level}) = 0;
for $k (qw(SCALAR ARRAY HASH)) {
# _dump can push into @post, so we hold our place using $postlen
my $postlen = scalar @post;
$post[$postlen] = "\*$sname = ";
local ($s->{apad}) = " " x length($post[$postlen]) if $s->{indent} >= 2;
$post[$postlen] .= $s->_dump(*{$name}{$k}, "\*$sname\{$k\}");
}
}
$out .= '*' . $sname;
}
elsif ($val =~ /^-?[1-9]\d{0,8}$/) { # safe decimal number
$out .= $val;
}
else { # string
if ($s->{useqq}) {
$out .= qquote($val);
}
else {
$val =~ s/([\\\'])/\\$1/g;
$out .= '\'' . $val . '\'';
}
}
}
# if we made it this far, $id was added to seen list at current
# level, so remove it to get deep copies
delete($s->{seen}{$id}) if $id and $s->{deepcopy};
return $out;
}
#
# non-OO style of earlier version
#
sub Dumper {
return Data::Dumper->Dump([@_]);
}
#
# same, only calls the XS version
#
sub DumperX {
return Data::Dumper->Dumpxs([@_], []);
}
sub Dumpf { return Data::Dumper->Dump(@_) }
sub Dumpp { print Data::Dumper->Dump(@_) }
#
# reset the "seen" cache
#
sub Reset {
my($s) = shift;
$s->{seen} = {};
return $s;
}
sub Indent {
my($s, $v) = @_;
if (defined($v)) {
if ($v == 0) {
$s->{xpad} = "";
$s->{sep} = "";
}
else {
$s->{xpad} = " ";
$s->{sep} = "\n";
}
$s->{indent} = $v;
return $s;
}
else {
return $s->{indent};
}
}
sub Pad {
my($s, $v) = @_;
defined($v) ? (($s->{pad} = $v), return $s) : $s->{pad};
}
sub Varname {
my($s, $v) = @_;
defined($v) ? (($s->{varname} = $v), return $s) : $s->{varname};
}
sub Purity {
my($s, $v) = @_;
defined($v) ? (($s->{purity} = $v), return $s) : $s->{purity};
}
sub Useqq {
my($s, $v) = @_;
defined($v) ? (($s->{useqq} = $v), return $s) : $s->{useqq};
}
sub Terse {
my($s, $v) = @_;
defined($v) ? (($s->{terse} = $v), return $s) : $s->{terse};
}
sub Freezer {
my($s, $v) = @_;
defined($v) ? (($s->{freezer} = $v), return $s) : $s->{freezer};
}
sub Toaster {
my($s, $v) = @_;
defined($v) ? (($s->{toaster} = $v), return $s) : $s->{toaster};
}
sub Deepcopy {
my($s, $v) = @_;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -