| Category: | Utility Script |
| Author/Contact Info | Yves Orton 2001 demerphq@hotmail.com demerphq |
| Description: | Data::PrettySimple is as its name suggests intended for printing out simple data structures in a pretty way, for perhaps embedding in other code or the like. Not intended as a drop in for Data::Dumper Does not handle objects or cyclic structures or any of the funkyness that Data::Dumper does, it is strictly intended for formatting data structures in a relatively readable way. Use it where you would prefer not to get Dumpers rather unelegant output like this But instead would prefer this: Keys and => lined up accros levels, arrays printed horizontal when it makes sense, stuff like that. Here what it exports and how to use it (from the comments)
DumpLOL/DumpHOH [SCALAR][CODE]REFLIST Sorry for the lack of POD, maybe on a repost. Yves |
package Data::PrettySimple; use Carp; use strict; use warnings; require Exporter; our $VERSION=0.1; our @ISA = qw(Exporter); our @EXPORT = qw(DumpHOH DumpLOL); ## Copyright and Copyleft Yves J. Orton ## demerphq@hotmail.com ## Use at your own risk. ## released under the same terms as Perl itself. ## ## The author accepts no responsibility for it use or misuse. ## ## Data::Dumper::Dumper sortof replacement ## ## Basically it is meant for aesthetically printing out ## Hashes of Hashes and Arrays of Arrays ## While it can handle intermixtures of the two its ## **not** what is intended, use Data::Dumper if thats really ## what you want. Where it excels is printing out ## hashes and arrays for use inside of source code. ## ## hashes are printed with all keys lined up with those of the ## same depth. Arrays and LOLs are printed out a bit more ## aesthetically than Data::Dumper with simple members being layed ## out horizontal with a right margin settable below. ## ## Calling conventions for both: ## DumpLOL/DumpHOH [SCALAR][CODE]REFLIST ## [SCALAR]:An optional new varname may be passed as the first param ## [CODE] :An optional coderef may be passes as the first or second p +aram ## REFLIST :a list of refrences of the apropriate type ## ## The coderef is used to format whatever data is held in the structur +e ## if it is provided then ALL data is passed to it in turn to be forma +tted ## this means quoting rules and etc must be obeyed by it. The codref s +hould ## return the stringified form its input. If one isnt provided then ## formatter of my own will be used. Frankly some of them are suspect ## so use at your own risk. (Although for simple data such as strings ## and numbers it should be ok. Interpolation is not being handled tho +ugh. ## $ will get printed as $.) ## ## If called in scalar context it returns the stringified form of all +of the ## refs passed. In list context it returns a list of strinigified form +s of ## its input. ## ## example uses: ## print DumpLOL("MY_LOL=",[[qw[abc]],[qw[abc]]]); ## print DumpHOH(sub{return "'".shift."'"},{a=>1,be=>2,ced=>3}); ## print DumpLOL([qw[Perl is very fun Its interesting and confusing an +d wow im ## not very creative with this example am I?]]); ## ## TODO: -PODIFY ## -DATA FORMATTER needs to be improved upon, quoting rules and ## escaping rules are maybe not being observed correctly. ## -DISPATCH function. Not sure if this should even be added. ## this **is not** a drop in replacement for Data::Dumper!! ## -SCALAR REFS are not handled currently. ## -OBJECTS are not covered either ## -CYCLIC structures... (what you think going reinvent GSARS wh +eel? ## not bloody likely.) ## ## Anyway outside of the caveats it does work and I routinely find use +s for it our $HBRACE_INDENT = 1; # how far in to move from a { our $SBRACE_INDENT = 2; # how far in to move from a [ our $ASSOC_SYMBOL = ' => '; # how the assoc should look, padded or + what our $INDENT_CHAR = " "; # what char to use to indent, dont mes +s our $BASE_INDENT = 0; # how far in the declaration should our $DECL_TYPE = "my "; # trailing space is needed here if wan +ted our $VAR_NAME = "VAR"; # Variable name to use our $ASSIGN_SYMBOL = " = "; # how padded should the = be? our $USE_COMMA = 0; # put commas in the ends or not? our $BARE_KEYS = 1; # quote only if necessary? our $FORMAT_SUB = undef; # a callback to format the output with our $BR_AT_WIDTH = 70; # arrays only our $QUOTE = '"'; # might have to change this # Im not real sure on quoting rules here sub ctrl_hex { my $str=shift; $str="undef" if !defined($str); $str=~s{([[:cntrl:]])}{sprintf("\\x%02x",ord($1))}sge; return $str; } sub fix_key { my $str=shift; # _ cant start for b/wards sppt # <chipmunk> be careful when using Data::Dumper with 5.005, though +. # perl5.005 thinks { _key => 'value' } is a bare block rather # than an anonymous hash if ($str!~/^[[:alpha:]-]+[\w\d]*$/ || !$BARE_KEYS) { $str='"'.ctrl_hex($str).'"'; } return $str; } sub fix_data { #this needs work. I'm not quite sure how to ake this bullet proof #for now its set $QUOTE as appropriate or use $FORMAT_SUB for some +thing #better my $data=shift; return ($FORMAT_SUB) ? $FORMAT_SUB->($data) #thanks G. Sarathy in Data::Dumper.... : (!defined($data)) ? "undef" : ($data =~ /^-?[1-9]\d{0,8}$/) ? $data : $QUOTE.ctrl_hex($data).$QUOTE; } sub _calcwidthsHOH { # Dont call me directly, use calcwidths() my ($hash,$widths,$level)=@_; my @keys=keys %$hash; my $width=0; foreach my $key (@keys) { my $len=length(fix_key($key)); $width=$len if $len>$width; if (UNIVERSAL::isa($hash->{$key},"HASH")) { _calcwidthsHOH($hash->{$key},$widths,$level+1) } elsif (ref($hash)) { # Maybe I should warn that things wont be # as pretty as liked? } } $widths->[$level]=$width if (!$widths->[$level] || $widths->[$level]<$width); } sub calcwidthsHOH { # resets the widths first my @widths=(); _calcwidthsHOH(shift,\@widths,0); return \@widths; } sub _dumphash { my ($hash,$widths,$ofs,$level)=@_; my $width=$widths->[$level]; my @keys=map {$_->[0]} sort { ($a->[1] || $b->[1]) ? $a->[0] cmp $b->[0] : $a->[0] <=> $b->[0] } map { [$_,$_=~/[[:^digit:]]/] } keys %$hash; my $st="{\n"; $ofs+=$HBRACE_INDENT; for my $i (0..$#keys) { my $key=$keys[$i]; my $fixkey=fix_key($key); my $keystr=join("",$INDENT_CHAR x $ofs, $fixkey, $INDENT_CHAR x ($width - length($fixkey)), , $ASSOC_SYMBOL); my $keylen=length($keystr); $st.=$keystr; if (UNIVERSAL::isa($hash->{$key},"HASH")) {#handle the sub has +hes $st.=_dumphash($hash->{$key},$widths,$keylen,$level+1); } elsif(UNIVERSAL::isa($hash->{$key},"ARRAY")){ $st.=_dumplol($hash->{$key},$ofs+$width+length($ASSOC_SYMB +OL),$level) } elsif(ref($hash->{$key})) { croak "Sorry. Dont know what to do with a '".ref($hash->{$ +key})."'.\n". "Try using Data::Dumper.\n"; } else { # its a scalar. What does it have in it. my $val=$hash->{$key}; $st.=fix_data($hash->{$key}); } $st.=($USE_COMMA || $i+1<@keys) ? ",\n" : "\n"; } $ofs-=$HBRACE_INDENT; return $st.($INDENT_CHAR x $ofs)."}"; } sub _DumpHOH { my ($hash,$ofs,$depth)=@_; return _dumphash($hash,calcwidthsHOH($hash),$ofs,$depth); } sub DumpHOH { my @rets; local $VAR_NAME = shift if (!ref($_[0])); local $FORMAT_SUB= shift if (UNIVERSAL::isa($_[0],'CODE')); my $varnum=1; foreach my $hash (@_) { my $ret=($VAR_NAME) ? ($INDENT_CHAR x $BASE_INDENT). $DECL_TYPE. '$'.$VAR_NAME. ((@_>1) ? $varnum : ""). $ASSIGN_SYMBOL : ""; $ret.=_DumpHOH($hash,length($ret),0); $ret.=";\n" if $VAR_NAME || @_; push @rets,$ret; } return wantarray ? @rets : "@rets"; } our $Array_Join=","; sub _dumplol { # assumes values are either lists or stringifiable scalars. my $array=shift; my $ofs =shift; my $depth=shift || 0; #try to build it simply my $multiline=0; my $result=""; foreach my $i (0..$#$array) { my $val=$array->[$i]; if (ref($val)) { $multiline=1; last; } $result.=fix_data($val); $result.="," if ($USE_COMMA || $i+1<@$array); if (length($result)>=$BR_AT_WIDTH) { $multiline=1; last; } } return '[ '.$result.' ]' unless $multiline; # so its either too long or its got refs in it. # either way its not gunna fit on one line $result="[\n"; $ofs+=$SBRACE_INDENT; $result.=($INDENT_CHAR x $ofs); my $tmp=""; for my $i (0..$#$array) { my $val=$array->[$i]; if (ref($val)) { if ($tmp) { $result.=$tmp."\n".($INDENT_CHAR x $ofs); $tmp=""; } if (UNIVERSAL::isa($val,"ARRAY")) { $result.=_dumplol($val,$ofs,$depth+1); } elsif (UNIVERSAL::isa($val,"HASH")) {#handle the sub has +hes $result.=_DumpHOH($val,$ofs,0); #dont pass our depth, # _DumpHOH uses it for a different purpose. # actually we dont use ours at all... # maybe it should be removed? } else { croak "Sorry. Dont know what to do with a '".ref($val) +."'.\n". "Try using Data::Dumper.\n"; } } else { $tmp.=fix_data($val); } if ($tmp) { $tmp.=$Array_Join if ($i<@$array-1 || $USE_COMMA); if (length($tmp)+$ofs >= $BR_AT_WIDTH) { $result.=$tmp."\n".($INDENT_CHAR x $ofs); $tmp=""; } }else{ $result.=$Array_Join."\n".($INDENT_CHAR x $ofs) if ($i<@$array-1 || $USE_COMMA); } } $result.=$tmp if ($tmp); $ofs-=$SBRACE_INDENT; $result.="\n".($INDENT_CHAR x $ofs)."]"; return $result; } sub DumpLOL { my @rets; local $VAR_NAME = shift if (!ref($_[0])); local $FORMAT_SUB= shift if (UNIVERSAL::isa($_[0],'CODE')); my $varnum=1; foreach my $array (@_) { my $ret=($VAR_NAME) ? ($INDENT_CHAR x $BASE_INDENT). $DECL_TYPE. '$'.$VAR_NAME. ((@_>1) ? $varnum : ""). $ASSIGN_SYMBOL : ""; $ret.=_dumplol($array,length($ret),0); $ret.=";\n" if $VAR_NAME || @_; push @rets,$ret; } return wantarray ? @rets : "@rets"; } 1; package Data::PrettySimple::Tester; use Data::Dumper; print Data::PrettySimple::DumpHOH({long___key=>{s=>'LONG VALUE', med=>'MED', long=>'LONG', longer=>'LONGER' }, short=>{s=>'LONG VALUE', med=>'MED', long=>[[qw(a b c d e f + g h)], [qw(a b c d e f + g h)]], longer=>[qw(a b c d e +f g h)] }}); print Dumper({long___key=>{s=>'LONG VALUE', med=>'MED', long=>'LONG', longer=>'LONGER' }, short=>{s=>'LONG VALUE', med=>'MED', long=>[[qw(a b c d e f + g h)], [qw(a b c d e f + g h)]], longer=>[qw(a b c d e +f g h)] }}); my $list=[ {key=>[qw(array array)]}, [{key=>[[qw(array array)],[qw(array array)]]}], {key=>[{key=>[qw(array array)]}]}, [ {key=>[qw(array array)]}, [{key=>[[qw(array array)],[qw(array array)]]}], {key=>[{key=>[qw(array array)]}]}, ] ]; my $hash={array=> [ {key=>[qw(array array)]}, [{key=>[[qw(array array)],[qw(arra +y array)]]}], {key=>[{key=>[qw(array array)]}]}, [ {key=>[qw(array array)]}, [{key=>[[qw(array array)],[qw(arra +y array)]]}], {key=>[{key=>[qw(array array)]}]}, ] ]}; local $Data::PrettySimple::DECL_TYPE=""; my $LOL=Data::PrettySimple::DumpLOL('LOL_TEST',$list); my $HOH=Data::PrettySimple::DumpHOH('HOH_TEST',$hash); my $lol=Data::Dumper::Dumper($list); my $hoh=Data::Dumper::Dumper($hash); my $LOL_TEST; my $HOH_TEST; my $lolt; my $hoht; die "HOH:$@" if !eval $HOH; die "LOL:$@" if !eval $LOL; {no strict; $lolt=Data::Dumper::Dumper($LOL_TEST); $hoht=Data::Dumper::Dumper($HOH_TEST); } die "Failed LOL" if $lol ne $lolt; die "Failed HOH" if $hoh ne $hoht; print join ("\n",$LOL,$lol,$lolt,$HOH,$hoh,$hoht); 1; |
|
|
|---|