We often need to dump data in Perl. The standard library has Data::Dumper and Dumpvalue, but when I have good access to CPAN, I prefer to use Data::Dump or YAML. Last year, I heard about Data::Printer and used that for a while, but switched back to Data::Dump because I couldn't distinguish between a blank string and a string with just nulls in it. If I had data that looked like
my $stuff = { blank => "", null => "\0", undefined => undef, zed => 0, zero => '0', };
Data::Printer gave me
\ { blank "", null "", undefined undef, zed 0, zero 0 }
whereas Data::Dump gave
{ blank => "", null => "\0", undefined => undef, zed => 0, zero => 0 }
and YAML gave
--- blank: '' null: "\0" undefined: ~ zed: 0 zero: 0
Yes, the Data::Printer output is very colorful, but I really need to see that null! I was thinking that Data::Printer was eating it, but today I had occasion to run a Data::Printer program inside EShell and I noticed the null was still there!
\ { blank "", null "^@", undefined undef, zed 0, zero 0 }
So I just need to get my terminal to show it to me. Or get Data::Printer to output something that my terminal is happier showing me. First, I tried adding a filter to Data::Printer like so
use Data::Printer filters => {SCALAR => sub { ${$_[0]} =~ s/\0/\\0/gr;}};
but that called my SCALAR filter instead of the built-in SCALAR filter. I couldn't figure out how to call my SCALAR filter and then hand off to the built-in one. So I hacked the the Data::Printer source code to change the nulls in the built-in SCALAR filter.
I ended up surveying a bunch of different data dumpers with a few more whitespace characters added to the mix.
#!/usr/bin/env perl use v5.14; use warnings; my $stuff = { blank => "", null => "\0", undefined => undef, zed => 0, zero => '0', tab => "\t", newline => "\n", carriage_return => "\r", form_feed => "\f", }; use Data::Dumper; $Data::Dumper::Sortkeys = 1; say "\nData::Dumper"; say Data::Dumper::Dumper $stuff; use Dumpvalue; say "\nDumpvalue"; say Dumpvalue->new->dumpValue($stuff); use Data::Dump qw(pp); say "\nData::Dump"; pp $stuff; use YAML; say "\nYAML"; say YAML::Dump $stuff; use Data::Printer; say "\nData::Printer"; p $stuff; use JSON; say "\nJSON"; say to_json $stuff, {pretty => 1}; use XML::Dumper; say "\nXML::Dumper"; say pl2xml $stuff; use Data::Dumper::Concise; say "\nData::Dumper::Concise"; say Data::Dumper::Concise::Dumper $stuff; use Data::Dump::Streamer; say "\nData::Dump::Streamer"; Data::Dump::Streamer::Dump $stuff; use Data::PrettyPrintObjects; say "\nData::PrettyPrintObjects"; say PPO $stuff; use Data::TreeDumper; say "\nData::TreeDumper"; say DumpTree $stuff;
Running this (with the unmodified Data::Printer) gives
Data::Dumper $VAR1 = { 'blank' => '', 'carriage_return' => ' ', 'form_feed' => '', 'newline' => ' ', 'null' => '', 'tab' => ' ', 'undefined' => undef, 'zed' => 0, 'zero' => '0' }; Dumpvalue 'blank' => '' 'carriage_return' => "\cM" 'form_feed' => "\cL" 'newline' => ' ' 'null' => "\c@" 'tab' => "\cI" 'undefined' => undef 'zed' => 0 'zero' => 0 Data::Dump { blank => "", carriage_return => "\r", form_feed => "\f", newline => "\n", null => "\0", tab => "\t", undefined => undef, zed => 0, zero => 0, } YAML --- blank: '' carriage_return: "\r" form_feed: "\f" newline: "\n" null: "\0" tab: ' ' undefined: ~ zed: 0 zero: 0 Data::Printer \ { blank "", carriage_return "", form_feed " ", newline " ", null "", tab " ", undefined undef, zed 0, zero 0 } JSON { "null" : "\u0000", "undefined" : null, "carriage_return" : "\r", "zed" : "0", "tab" : "\t", "blank" : "", "zero" : "0", "form_feed" : "\f", "newline" : "\n" } XML::Dumper <perldata> <hashref memory_address="0x185ede8"> <item key="blank"></item> <item key="carriage_return"> </item> <item key="form_feed"></item> <item key="newline"> </item> <item key="null"></item> <item key="tab"> </item> <item key="undefined" defined="false"></item> <item key="zed">0</item> <item key="zero">0</item> </hashref> </perldata> Data::Dumper::Concise { blank => "", carriage_return => "\r", form_feed => "\f", newline => "\n", null => "\0", tab => "\t", undefined => undef, zed => 0, zero => 0 } Data::Dump::Streamer $HASH1 = { blank => '', carriage_return => "\r", form_feed => "\f", newline => "\n", null => "\0", tab => "\t", undefined => undef, zed => 0, zero => 0 }; Data::PrettyPrintObjects { blank => '', carriage_return => ' ', form_feed => '', newline => '\n', null => , tab => ' ', undefined => undef, zed => 0, zero => 0 } Data::TreeDumper |- blank = [S1] |- carriage_return = [\r] [S2] |- form_feed = [S3] |- newline = [\n] [S4] |- null = [S5] |- tab = [\t] [S6] |- undefined = undef [S7] |- zed = 0 [S8] `- zero = 0 [S9]
I'm not sure if I'd rather look at whitespace or escape sequences for things like tabs and newlines, but I'm positive I want to be able to see the difference between an empty string (which is false in Perl) and a string containing a null (which is true in Perl).
Update: (22 Jan 2012) garu applied my patch, so now Data::Printer shows the nulls as \0
$ cpanm Data::Printer --> Working on Data::Printer Fetching http://search.cpan.org/CPAN/authors/id/G/GA/GARU/Data-Printer-0.27.tar.gz ... OK Configuring Data-Printer-0.27 ... OK Building and testing Data-Printer-0.27 ... OK Successfully installed Data-Printer-0.27 (upgraded from 0.26) 1 distribution installed $ perl -MDDP -e '$h = {null => "\0"}; p $h' \ { null "\0" }
Update: (23 Jan 2012) garu spruced it up even more (see comments)!
$ cpanm Data::Printer --> Working on Data::Printer Fetching http://search.cpan.org/CPAN/authors/id/G/GA/GARU/Data-Printer-0.28.tar.gz ... OK Configuring Data-Printer-0.28 ... OK Building and testing Data-Printer-0.28 ... OK Successfully installed Data-Printer-0.28 (upgraded from 0.27) 1 distribution installed $ perl -MDDP -e '$h = {null => "\0"}; p $h' \ { null "\0" } $ perl -MDDP -e '$h = {foo => "\t"}; p $h' \ { tab " " } $ perl -MDDP=escape_chars,0 -e '$h = {foo => "\t"}; p $h' \ { tab "\t" }
Not only that, but version 0.28 now has the 'escape_chars' customization option. By default, Data::Printer will escape all characters (i.e. render them on the output).
If you add "escape_chars => 0" to your initial setup (either during "use DDP" or via your .dataprinter file) then it will show your escaped characters instead of rendering them (i.e. display them as \t, \n, \f, \b, etc). The null character, however, will always be displayed as "\0".
Thanks for your initial patch and for inspiring this new feature. Hope you can switch back to Data::Printer now :-)
Posted by: Account Deleted | 01/22/2012 at 11:02 PM
Very nice! I like version 0.28!
Posted by: oylenshpeegul | 01/23/2012 at 03:46 PM
Ok, new update :)
Some people complained about the property name ("escape_chars"), so I had to deprecate it in favour of the clearer "print_escapes". The old name will work until v0.32, but will trigger a warning so people can update their code. Sorry for the inconvenience, the previous name was indeed hard to figure out because 'escape' could be interpreted as a noun or as an adjective, and as such there was a lot of confusion as to what it should do when enabled. The print_escapes property, of course, will print "\t" instead of rendering the tab.
Oh, just so you know, another important update in version 0.30 is that print_escapes also works in hash keys, so weird stuff like:
my %hash = ( "\n" => 1 );
will be properly escaped when print_escapes is set to 1 :-)
Finally, again on the "let's give some more lovin' to hashes" train, the new 'quote_keys' property in 0.30 can be set to 1, 0 or 'auto' (default), in which case it will surround hash keys with uncolored single-quotes whenever they're empty or contain (rendered) spaces/newlines.
Cheers!
Posted by: Account Deleted | 02/12/2012 at 10:03 PM