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

Dearest Monks, I have an annoying string formatting issue for atom names in protein databank file. The names can be 1-4 characters long and should print as follows (using metacharacters):
'\s\w\s\s' '\s\w\w\s' '\s\w\w\w' '\w\w\w\w'
Clearly, I can test the length of the string and then use " %-3s" or "%4s" accordingly. Is something simpler possible?

Replies are listed 'Best First'.
Re: conditional string formatting
by AnomalousMonk (Archbishop) on Jun 08, 2015 at 18:08 UTC

    I'm not sure this is in any sense "simpler" (it certainly burns a lot more computrons than just checking the length and selecting a format string conditionally), but:

    c:\@Work\Perl\monks>perl -wMstrict -le "my @names = qw(A AB ABC ABCD); ;; for my $name (@names) { my $s = sprintf '%4s', sprintf '%-3s', $name; print qq{'$s'}; } " ' A ' ' AB ' ' ABC' 'ABCD'
    See sprintf for format specifiers.


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

      Wow the double sprintf maneuver
      Perl is limited only by our fantasy.. ++AnomalousMonk

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
        "double sprintf maneuver "

        Or perhaps The anomalous double sprintf maneuver?

        Sounds a bit like a bizarre chess combination.

        Regards, Karl

        «The Crux of the Biscuit is the Apostrophe»

        It would have been nicer if I could have figured out a way to use a single (s)printf, but I couldn't. :-\


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

Re: conditional string formatting
by choroba (Cardinal) on Jun 08, 2015 at 17:24 UTC
    There is no 'center' option for printf. You have to check the length:
    printf +(3 < length $in ? q() : ' ') . '%-3s', $in

    Or, more funny (and shorter):

    printf ' ' x (4 > length $in) . '%-3s', $in
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      Thanks!
Re: conditional string formatting
by QM (Parson) on Jun 09, 2015 at 08:42 UTC
    Simple and maintainable:
    my %formats = ( 1 => ' %s ', 2 => ' %s ', 3 => ' %s', 4 => '%s', ); for my $x ("a", "bb", "ccc", "dddd") { printf $formats{length($x)}, $x; print "\n"; } ###################### a bb ccc dddd

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

      ++Very nice, and very easily extended to handle a default case!

      c:\@Work\Perl\monks>perl -wMstrict -le "my %formats = ( 1 => ' %s ', 2 => ' %s ', 3 => ' %s', 4 => '%s', +); ;; for my $x ('a', 'bc', 'def', 'ghij', 'klmno') { printf qq{'@{[ $formats{length($x)} // '%4.4s' ]}' \n}, $x; } " ' a ' ' bc ' ' def' 'ghij' 'klmn'


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

        ...easily extended to handle a default case!
        Yes, I forgot to handle that. The defined-or touch is nice.

        While the @{[]} wrapper does the job, is it completely necessary? (Perhaps I have missed something in my uncaffeinated state?) Other than a quick crutch for interpolating inside qq{} while adding single quotes and appending a newline?

        -QM
        --
        Quantum Mechanics: The dreams stuff is made of

Re: conditional string formatting
by AnomalousMonk (Archbishop) on Jun 09, 2015 at 00:44 UTC

    Actually, I think I like this one better than either choroba's or mine above:

    c:\@Work\Perl\monks>perl -wMstrict -le "my @names = qw(A AB ABC ABCD ABCDE); ;; for my $name (@names) { printf qq{%8s -> }, qq{'$name'}; my $s = sprintf length($name) > 3 ? '%s' : ' %-3s', $name ; print qq{'$s'}; } " 'A' -> ' A ' 'AB' -> ' AB ' 'ABC' -> ' ABC' 'ABCD' -> 'ABCD' 'ABCDE' -> 'ABCDE'
    Make the first  '%s' into  '%4.4s' to limit the field to the first four characters only.


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

Re: conditional string formatting
by Tux (Canon) on Jun 08, 2015 at 16:36 UTC
    printf "%4.4s", $var."  ";

    Enjoy, Have FUN! H.Merijn
Re: conditional string formatting
by GotToBTru (Prior) on Jun 08, 2015 at 16:42 UTC

    Your strings are all 8 characters, not 4 ...? Clearly I'm missing something.

    Dum Spiro Spero
      my apologies, I edited the initial post (which was pretty unclear the first time).

        Please do NOT (EVER!) edit a previously-created question or reply without plainly marking what's new with a note about the fact of your editing and <strike>...</strike> tags around deletions:

        Added: "new text... and changed value of my $var at Ln 42, from 6 to 7

        Note that you CAN NOT use markup tags inside code tag pairs but you can within most other markup... so, please, be vigilant about using strikeout tags around that which you're disavowing or deleting. Strike tag pairs render this way:

        that which you're removing, changing, editing, emending, etc.

        In this case, your edit removed the context for GotToBTru's reply. Yes, you provided clarification, but it makes a thread easier to read and understand if the edit info is with the original, and better yet, if you time-stamp your update note, so a careful reader can see if a reply pre-dates your change.

        For more, see Markup in the Monastery.

        EDITED: fixed markup errors that I missed on preview.

Re: conditional string formatting
by superfrink (Curate) on Jun 10, 2015 at 05:56 UTC
    You might find something in perlform. The following is close but puts the extra whitespace on the left instead of the right in the case of a length of one.

    Update: In this code format is not print the trailing \s characters. Sample output updated to show the whitespace is missing.
    #! /usr/bin/perl -w use strict; for my $val (qw/a ab abc abcd/) { format = @|||| $val . write \*STDOUT; }
    Output:
    $ ./centre-via-format.pl a ab abc abcd $ ./centre-via-format.pl | od -ch 0000000 a \n a b \n a b c \n a b c 2020 0a61 6120 0a62 6120 6362 610a 6362 0000020 d \n 0a64 0000022