NAME P - Safer, friendlier printf/print/sprintf + say
VERSION
Version "1.1.41"
SYNOPSIS
use P qw[:depth=5 :undef="(undef)"];
P FILEHANDLE FORMAT, LIST
P FILEHANDLE, LIST # comma is not disallowed
P FORMAT, (LIST)
P (LIST)
P @ARRAY # may contain File Handle, Format and ARGS
# all in 1 array.
$s = P @ARRAY; P $s; # can be same output as "P @ARRAY"
Pe # same as P STDERR,...
$s = P FILEHANDLE ... # sends same output to $s and FILEHANDLE
DESCRIPTION
P
is a combined print, printf, sprintf & say in 1 routine. It saves tremendously on development time. It's not just a 1 character 'verb', but has other time-saving and powerful features. It has the convenience of say
in adding a newline when needed but also allows specifying a File Handle and format statement. P
accepts a filehandle with or without a comma after the filehandle.
A fixed string can be changed to formatted output with no change of verb:
Example:
# Starting with a "die" statement.
1) die "Wrong number of params";
# Then number of arguments passed is added:
2) die P "Expecting 2 params, got %s", scalar @ARGV;
# Then contents of @ARGV can be printed as well (no loop needed):
3) die P "Expecting 2 params, got %s (ARGV=%s), 0+@ARGV, \@ARGV;
P
can replacing sprintf
without the need for a temporary variable and without printing cryptic representations for @ARGV, like "ARRAY(0x5A2B3C66)
". Instead, P
displays the actual contents of the array, showing ["arg1", "arg2"].
When it comes to newline
's, or "\n" at the end of line, P
attempts to look at context. If output is assigned to a variable, then P
acts like sprintf
. If it is to a file handle, then it will act like say
and automatically append a newline
. Unlike say
, P
can use formatted output and print to a file handle. Printing to STDERR is simplified with the Pe
form of P
, which is a short form of 'P STDERR, "..."'.
When P
prints to strings, any one newline at the end of the string will be suppressed. Conversely it will add one if printing to a device. If printing to a string and a device at the same time, it will favor suppression and not output a newline to the device.
Rather than aborting output when undef
is part of the output, P
prints a configurable symbol, '∄', by default, the symbol for "does not exist" where the undef would have printed and the rest of the string is printed normally.
P
tries not to have arbitrary restrictions on it's arguments.
It handles cases that the equivalent perl statement won't.
VERB -> P print printf sprintf say
V --FEATURE-- V --- ----- ------ ------- ---
1) to a FH Yes Yes Yes No Yes
2) to $fh Yes Yes Yes No No
3) to a string Yes No No Yes No
4) add EOL-NL to FH? Yes No No No Yes
5) sub EOL-NL in string Yes No No No No
6) FMT Yes No Yes Yes No
7) @[FMT,ARGS] Yes No Yes No No
8) undef to "%s" Yes No No No No
9) @[$fh,FMT,ARGS] Yes No No No No
10) like "tee" Yes No No No No
(9) - File Handle in 1st member of ARRAY used for output.
(10) - When P is being used as a string formatter like sprintf,
it can still have a "$fh" as the first argument that will
print the formatted string to the file handle as well as
returning it's value (note: this will force the string
to be printed w/o a trailing newline).
Undefs
When printed as strings ("%s"
), undefs are automatically caught and "∄", (U+2204 - meaning "There does not exist") is printed in place of "Use of uninitialized value $x in xxx at -e line z.
"
By default P
, prints the content of references (instead HASH (or ARRAY)=(0x12345678). By default, it prints three levels deep with deeper nesting replaced by by the unicode ellipsis character (U+2026).
While designed for development use, it is useful in many more situations, as tries to "do the right thing" based on context. It can usually be used as a drop-in replacement the perl functions print
, printf
, sprintf
, and, say
.
P tries to smartly handle newlines at the end of the line -- adding them or subtracting them based on if they are going to a file handle or to another variable.
The newline handling at the end of a line can be supressed by adding the Unicode control char "Don't break here" (0x83) at the end of a string or by assigning the return value and having a file handle as the first argument. Ex: my $fmt = P STDOUT, "no LF added here-->"
.
Bless
ed objects, by default, are printed with the class or package name in front of the reference. Note that these substitutions are performed only with references printed through a string ("%s"
) format -- features designed to give useful output in development or debug situations.
One difference between P
and sprintf
: P
can take an array with the format in the 0th element, and parameters following. Sprintf
will cause an error to be raised, if you try passing an array to it, as it will force the array into scalar context -- which as the manpage says "is almost never useful", and the perl-developers admit "is never useful". Rather than follow in the design flaws of its predecessors, P tries to do the right thing.
NOTE: A side effect of P being a contextual replacement for sprintf, is if it is used as the last line of a subroutine. By default, this won't print it's arguments to STDOUT unless you explicity specify the filehandle, as it will think it is supposed to return the result -- not print it. An alternate workaround -- return another value, like a status value. OR use it as a feature. A subroutine that has a P-statment
at the end can be used in string construction or can be used for direct output.
EXAMPLE: Duel-Use Subroutines for Strings or Printing
sub items_in_list() {
my $numitems = get_num();
P "num items=%s", $numitems;
} # can be used:
my $s=items_in_list();
# or
$items_in_list(); # prints to STDOUT w/newline on end
Special Use Features
While P
is normally called procedurally, and not as an object, there are some rare cases where one would really like it to print "just 1 level deeper". To do that, you need to get a pointer to P
's options
.
To get that pointer, call P::->ops({key=
value})> to set P
's options and save the return value. Use that pointer to call P. See following example.
EXAMPLE: (changing P's defaults)
Suppose you had an array of objects, and you wanted to see the contents of the objects in the array. Normally P would only print the first two levels:
my %complex_probs = (
questions =E<gt> [ "sqrt(-4)", "(1-i)**2" ],
answers =E<gt> [ {real => 0, i =>2 },
{real => 0, i => -2 } ] );
my $prob_ref = \%complex_problems;
P "my probs = %s", [$prob_ref];
The above would normally produce:
my probs = [{answers=>[{…}, {…}], questions=>["sqrt(-4)", "(1-i)**2"]}]
Instead of the contents of the hashes, P shows the ellipses (a 1 char-width wide character) for the interior of the hashes. If you wanted the interior to print, you'd need to raise the default data expansion depth for P
as we do here:
my %complex_probs = (
questions => [ "sqrt(-4)", "(1-i)**2" ],
answers => [ {real => 0, i =>2 }, { real => 0, i => -2 } ] );
my $p=P::->ops({depth=>4});
$p->P("my array = %s", \%complex_probs);
The above allows 1 extra level of depth to be printed, so the elements in the hash are displayed producing:
my probs = [{answers=>[{i=>2, real=>0}, {i=>-2, real=>0}], # extra "\n"
questions=>["sqrt(-4)", "(1-i)**2"]}]
NOTE: when referring to the package P
, a double colon is usually needed to tell perl you are not talking about the function name.
Please don't expect data printed by P to be "pretty" or parseable. It's not meant to be a Perl::Tidy or Data::Dumper. Especially, when printing references, it was designed as a development aid.
Summary of possible OO args to "ops" (and defaults)
depth => 3
-
Allows setting depth of nested structure printing. NOTE: regardless of depth, recursive structures in the same call to
P
, will not expand but be displayed in an abbreviated form. implicit_io => 0
-
When printing references, GLOBS and IO refs do not have their contents printed (since printing contents of such refs may do I/O that changes the object's state). If this is wanted, one would call
ops
withimplicit_io
set to true (1). noquote => 1
-
In printing items in hashes or arrays, data that are Read-Only or do not need quoting won't have quoting (contrast to Data::Dumper, where it can be turned off or on, but not turned on, only when needed).
maxstring => undef
-
Allows specifying a maximum length of any single datum when expanded from an indirection expansion.
Example 2: Not worrying about "undefs"
Looking at some old code of mine, I found this:
print sprintf STDERR,
"Error: in parsing (%s), proto=%s, host=%s, page=%s\n",
$_[0] // "null", $proto // "null", $host // "null",
$path // "null";
die "Exiting due to error."
Too many words and effort in upgrading a die message! Now it looks like:
die P "Error: in parsing (%s), proto=%s, host=%s, page=%s",
$_[0], $proto, $host, $path;
It's not just about formatting or replacing sprintf -- but automatically giving you sanity in places like error messages and debug output when the variables you are printing may be 'undef' -- which would abort the output entirely!
MORE EXAMPLES
P "Hello %s", "World"; # auto NL when to a FH
P "Hello \x83"; P "World"; # \x83: suppress auto-NL to FH's
$s = P "%s", "Hello %s"; # not needed if printing to string
P $s, "World"; # still prints "Hello World"
@a = ("Hello %s", "World"); # using array, fmt as 1st arg
P @a; # print "Hello World"
P 0 + @a; # prints #items in '@a': 2
P "a=%s", \@a; # prints contents of 'a': [1,2,3...]
P STDERR @a # use @a as args to a specific FH
# Uses indirect method calls when
# invoked like "print FH ARGS"
#
Pe "Output to STDERR" # 'Shortcut' for P to STDERR
%H=(one=>1, two=>2, u=>undef); # P Hash bucket usage + contents:
P "%H hash usage: %s", "".%H; # Shows used/total Hash bucket usage
P "%H=%s", \%H; # print contents of hash:
%H={u=>(undef), one=>1, two=>2}
bless my $h=\%H, 'Hclass'; # Blessed objects...
P "Obj_h = %s", $h; # & content:
Obj_h = Hclass{u=>(undef), one=>1, two=>2}
Sample Code + Test + Demo
To demonstrate the various usages of P, several examples are embedded with this documenation in a special DATA
section. If this module is executed as with perl P.pm
or just P.pm
if the module is set to be executable, it will run a short program that shows different features of P as well as doing a short run-time test (that is actually part of the test suite).
The demo/example/test code embedded in this module is NOT compiled or accessed when it is use
ed in code.
As of this writing there are 13 examples. The output of these examples follows:
#1 (ret from func) : Hello Perl 1
#2 (w/string) : Hello Perl 2
#3 (passed array) : Hello Perl 3
#4 (w/fmt+string) : Hello Perl 4
#5 (to STDERR) : Hello Perl 5
#6 (to strng embedded in #7):
#7 (prev string) : prev str="Hello Perl 6" (no LF) && Hello Perl 7
#8 (P && array ref) : ["one", "two", "three", 4, 5, 6]
#9 (P HASH ref) : {a=>"apple", b=>"bread", c=>"cherry"}
#10 (P Pkg ref) : Pkg{a=>1, b=>2, x=>'y'}
#11 (P @{[FH,["fmt:%s",…]]}) : fmt:Hello Perl 11
#12 (truncate embedded float): norm=3.14159265358979324, embed={pi=>3.14}
#13 (test mixed digit string): embed roman pi = ["3.ⅰⅳⅰⅴⅸ"]
The code uses a function iter
that prints Hello Perl followed by an autoincrementing counter that also is used as the test or example number.
Putting the format + its arguments in an array is simple and does not change if P is printing to output or to a string (cf. sprintf/printf). P goes further than allowing the format specification in an array -- it also allows putting the file handle as the 1st element in the array as shown in #11.
P is not picky about how file handles can be used -- they can be followed by a space or by a comma. No special syntax is needed for the arguments of P, it can follow the example of printf or the standard usage of using commas to separate arguments.
NOTES
Values given as args with a format statement, are checked for undef and have "∄" substituted for undefined values. If you print vars as in decimal or floating point, they'll likely show up as 0, which doesn't stand out as well.
Sometimes the perl parser gets confused about what args belong to P and which do not. Using parentheses (ex. P("Hello World")
) can help in those cases.
Usable in any code, P was was designed to save typing, time and work of undef checking, newline handling, peeking at data structures in small spaces during development. It tries to do the "right thing" with the given input. It may not be suitable where speed is paramount.