rir has asked for the wisdom of the Perl Monks concerning the following question:

I'm printing some data,
printf( "A float: %12.4f$/", $root_beer );
and, later, some moron, usually me, decides that if $root_beer is zero and/or an empty string the printed field should maintain its width but be blank.

A common place issue and my reflex is to do the same things I did in C decades ago: Use a variable as a conversion specifier ($fmt="%12s") or use sprintf then printf.

I am asking for people's best practices on printing blank fields for values like zero, undef and empty string in places where the conversion specifier would expect a numeric and other like issues.

Update: The nature of the replies I've gotten aren't what I hoped for. As a programming problem I'd like to hear the voice of experience. But I'd also like to hear about the quirks (impedance mismatch) of what people want to read in their reports versus what is trivial to output. ( Re-Update: Much later and I've found fizbin's code useful.)

My best solution to date:

my $fmt = fmt_num_or_blank( $width, $data); printf( "A float: $fmt$/", $root_beer );
Which is least invasive of the printf.

My best conception is something like:

# Mnemonic: N nothing for nought nor null printf( "A float: %N12.4f$/", $root_beer );
but at best I could only weakly implement that.

Over the years there has been a lot of hate mail for printf formatting as being messy and error prone. I think it just reflects the problem domain. Are there other output massagers to look at?

Be well,
rir

Replies are listed 'Best First'.
Re: Blankety-Blank Blanks and Zeros, etc.
by fizbin (Chaplain) on Aug 26, 2005 at 18:44 UTC

    This technique is adapted out of production C++ code that does something similar:

    sub blank_formatter { my ($printf_format, $length) = @_; if (!defined($length)) { $printf_format =~ /^%(\d+)/ or die "Can't deduce length from $prin +tf_format"; $length = $1; } my ($blank) = ' ' x $length; sub { $_[0] ? sprintf($printf_format,$_[0]) : $blank; }; } sub my_printf { my ($format, @args) = @_; my (@printfargs) = (); while (@args) { my $arg = shift @args; if (ref($arg) and ref($arg) eq 'CODE') {push @printfargs, $arg->(s +hift @args);} else {push @printfargs, $arg;} } printf($format, @printfargs); } # similarly, if you need it, my_sprintf # Was: # printf( "A float: %12.4f$/", $root_beer ); my_printf( "A float: %s$/", blank_formatter('%12.4f'), $root_beer );

    Then, if you need it, you can get whole aligned tables with something like:

    my $fmt = blank_formatter('%12.4f'); my_printf("%10s: %s %s %s %s\n", $rowname, map {$fmt => $_} ($numcol1, $numcol2, $numcol3, $numcol4));

    Similarly for other complicated formatting requirements. Just define your format generator so that it returns a sub reference that will take the thing to be formatted and returns a string, and then use printf as normal with %s placeholders.

    -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
Re: Blankety-Blank Blanks and Zeros, etc.
by halley (Prior) on Aug 26, 2005 at 18:19 UTC
    One thought:
    print +("A float: ", $root_beer? sprintf("%12.4f", $root_beer) : $"x12, $/);
    (And that's the first time I've seen someone stick $/ inside a double-quoted literal. I use $/ a lot, but I use \n if I'm already in the string literal.)

    --
    [ e d @ h a l l e y . c c ]

      Why not just switch out the format?

      printf +($root_beer ? "%12.4f" : " "x12)."\n", $root_beer;
      That's what I'd do if I didn't care too much about maintainability.

      Of course, the correct way to do it is something like this:

      if ($root_beer) { printf "%12.4f\n", $root_beer } else { print " "x12 . "\n"; }
      Or any way that you consider more readable. Where "readable" means that you can look at it 6 months later without thinking, "what the hell was I thinking when I wrote this?" :-)
Re: Blankety-Blank Blanks and Zeros, etc.
by davidrw (Prior) on Aug 26, 2005 at 18:19 UTC
    hmm.. just some brainstorming (maybe it will lead to something better):
    printf( "A float: %12s$/", $root_beer ? sprintf(".4f",$root_beer) : $r +oot_beer ); $root_beer = sprintf(".4f",$root_beer) if $root_beer; printf( "A float: %12s$/", $root_beer ); sub fmt { $_[0] ? sprintf(".4f",$_[0]) : $_[0] } printf( "A float: %12s$/", fmt($root_beer) );
Re: Blankety-Blank Blanks and Zeros, etc.
by Tanktalus (Canon) on Aug 26, 2005 at 18:24 UTC

    Attempt 1 printf("A float: %12" . ($root_beer ? ".4f" : "s") . $/, ($root_beer || '')); blech.

    Attempt 2

    if ($root_beer) { printf("A float: %12.4f$/", $root_beer); } else { printf("A float: %12s$/", ''); }
    Better, I think.

    Attempt 3 printf("A float: %12s$/", $root_beer ? sprintf("%12.4f",$root_beer) : ''); Hmmm - I don't think so.

    Attempt 4

    my $root_beer_str = $root_beer || ''; # change 0 to blank. $root_beer_str &&= sprintf("%12.4f", $root_beer); printf("A float: %12s$/", $root_beer_str);
    That seems mildly evil. I'm sure we could golf that down a bit... ;-)

    Attempt 5 - just because. printf("A float: %12s$/", ($root_beer || '') && sprintf("%12.4f", $root_beer)); Ewwwww.... :-)

Re: Blankety-Blank Blanks and Zeros, etc.
by TedPride (Priest) on Aug 26, 2005 at 21:41 UTC
    for (3, 45.6, '', 0) { $root_beer = $_; print 'A float: ', (!$root_beer ? ' 'x12 : sprintf '%12.4f', $root +_beer), $/; }