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

This probably sounds like a very basic question, but I just spent the last half-hour going through the perl documentation (and even through the system sprintf man page), and couldn't find it.

Let's suppose I have an array that I don't know the length of (or even one long enough that I don't want to write out the format for each variable). Yes, I know I could build an output string by looping over the array, but I am trying to learn about sprintf. :)

Two questions:

1. Is there some way of "repeating" a format (ie. use this parameter for the next ten variables or for all remaining variables)?

2. What happens when perl "runs out" of parameters (there are more variables to print than parameters in the format)? Does it start from the beginning (probably not), repeat the last format (think some languages do this), or just throw an error?

  • Comment on sprintf and arrays of unknown (in advance) length

Replies are listed 'Best First'.
Re: sprintf and arrays of unknown (in advance) length
by 1nickt (Canon) on Oct 12, 2018 at 01:10 UTC

    Hi, your problem description is not very clear. I think you are saying that you want to be able to handle arrays of varying lengths with a single sprintf statement. If so, you can always use the result of an expression as the format:

    use strict; use warnings; my @slow_day = ( 4, 1..4 ); my @busy_day = ( 12, 1..12 ); for my $report ( \@slow_day, \@busy_day ) { my ( $total, @sales ) = @{ $report }; my $fmt = 'Total: $%2d Sales:' . ' $%.02f' x scalar(@sales); print sprintf($fmt, $total, @sales), "\n"; } __END__
    Output:
    $ perl 1223893.pl Total: $ 4 Sales: $1.00 $2.00 $3.00 $4.00 Total: $12 Sales: $1.00 $2.00 $3.00 $4.00 $5.00 $6.00 $7.00 $8.00 $9.0 +0 $10.00 $11.00 $12.00
    Similarly you can examine the list and use the longest element length as a padding value, etc.

    As to your second question: What happens when perl ... "runs out" of parameters (there are more variables to print than parameters in the format) ... Well, what happens when you try it? You can just use a one-liner for that:

    $ perl -Mstrict -wE 'say sprintf("%s", qw/two strings/);'

    Hope this helps!


    The way forward always starts with a minimal test.
      > What happens when perl ... "runs out" of parameters

      Note that C behaves the same, but bash doesn't:

      $ printf '[%s]' two strings [two][strings]

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        Curious: I couldn't get an example C program calling printf("%s\n", "two", "strings"); to crash or display any errors under Valgrind or -fsanitize=address -fsanitize=undefined even with full optimization and with omitted frame pointers, despite it being undefined behaviour.

        Not passing enough arguments in C for %s, though, causes a crash pretty quickly, because printf tries to interpret as a pointer something that was left on the stack from a previous function call, whatever it was. I got a lucky (null) a few times (stack had been filled with zeroes, and some C libraries handle NULL for %s as a special case, which is not required by the standard), then crashed on a different run when it tried to dereference address 0x1.

Re: sprintf and arrays of unknown (in advance) length
by AnomalousMonk (Archbishop) on Oct 12, 2018 at 15:46 UTC

    When dealing with lists and particularly intermediate lists in map/grep chains, it's sometimes difficult (or even impossible?) to know the number of list elements at runtime. This can be handled with yet another map expression:

    c:\@Work\Perl\monks>perl -wMstrict -le "print map sprintf(q{`%s' }, $_), grep m{ \A [aeiou] }xms, qw(elements comprising this list unknown in advance) ; " `elements' `unknown' `in' `advance'
    This could be handled with an explicit intermediate array, but it's arguably more awkward:
    c:\@Work\Perl\monks>perl -wMstrict -le "printf do { my @elems = grep m{ \A [aeiou] }xms, qw(elements within this list unknown in advance) ; qq/@{[ q{`%s' } x @elems ]}/, @elems; }; " `elements' `unknown' `in' `advance'
    (This doesn't reveal any new-found capability of sprintf, but that's life.)


    Give a man a fish:  <%-{-{-{-<

Re: sprintf and arrays of unknown (in advance) length
by LanX (Saint) on Oct 12, 2018 at 16:00 UTC
    > Is there some way of "repeating" a format

    It doesn't seem so. like others already demonstrated it's possible to build a repeated format with the x operator.

    Personally I'd rather prefer to just do a for loop creating the output, hence avoiding to repeat myself

    DB<35> @a=1..10 DB<36> printf "%03d:",$_ for @a 001:002:003:004:005:006:007:008:009:010:

    please note that you can easily have other list constructors like map and grep in place for @a.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: sprintf and arrays of unknown (in advance) length
by BillKSmith (Monsignor) on Oct 12, 2018 at 12:44 UTC
    Your question suggests that you expect sprintf formats to work like FORTRAN. They do not. It took me a long time to discover lnict's solution.

    UPDATE: Corrected terminology. Thanks Hippo.

    Bill

      Note that Perl formats and sprintf are entirely different things. While the latter does not behave like Fortran with respect to repetition and numbers of arguments, the former is arguably somewhat closer.

      You may also be interested in Fortran::Format and Fortran::F90Format.

Re: sprintf and arrays of unknown (in advance) length
by Anonymous Monk on Oct 12, 2018 at 14:20 UTC
    Perhaps the baby cart?
    my @_a=qw(one two three four damn it shut the door!); printf "@{[q{'%s' } x @_a]}\n",@_a;
    which would yield
    'one' 'two' 'three' 'four' 'damn' 'it' 'shut' 'the' 'door!'
[OT: D] Re: sprintf and arrays of unknown (in advance) length
by AnomalousMonk (Archbishop) on Oct 12, 2018 at 20:35 UTC

    The D language has the nesting  %( %) variadic array format specifiers (see std.format.formattedWrite). (I haven't followed up, but D often tracks C/C++ features, so the latest versions of these languages may have these specifiers also.) Any chance that Perl might get something like this?


    Give a man a fish:  <%-{-{-{-<

      > Any chance that Perl might get something like this?

      Why? Perl has thanks to variable interpolation a huge flexibility, that's effectively a built in template language. Plus loads of operators to recombine strings.

      What's the point overloading the printf formats with more syntax if we can already have it all in a short, expressive and canonical way?

      DB<90> sub format_n { my $n= shift; printf "%b" x $n ." ", splice @_ +, 0, $n while @_ } DB<91> format_n 3,(1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0) 101 100 101 010 DB<92> format_n 4,(1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0) 1011 0010 1010 DB<93> format_n 5,(1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0) 10110 01010 10000

      (non-destructive version left as exercise)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: sprintf and arrays of unknown (in advance) length
by dbuckhal (Chaplain) on Oct 12, 2018 at 16:53 UTC
    Another 'x' option...
    perl -Mstrict -we ' my @arr = (0..10); printf("%d "x@arr, @arr); ' __output__ 0 1 2 3 4 5 6 7 8 9 10

    or 'join' option...

    perl -Mstrict -we ' my @arr = (0..10); printf("%s", join(" ", @arr)); ' __output__ 0 1 2 3 4 5 6 7 8 9 10

    EDIT:

    Yes, I know I could build an output string by looping over the array, but I am trying to learn about sprintf. :)

    Sorry if 'join'-ish type option above was something OP already tried...