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

I wrote a small script that works fine, but the format isn't exactly perfect. Below is the code

my $line = <>; while (defined($line = <>)) { chomp($line); my @items = (split /:/, $line); #my @data_items = (@items) [0,5]; #print "Customer Data: @data_items \n"; print "Customer Name: $items[0] \t Number of Books checked out: +$items[5]\n"; }

Which gives this output

Customer Name: wilma flinstone Number of Books checked out: + 3 Customer Name: barney rubble Number of Books checked out: 5 Customer Name: Sam Slate Number of Books checked out: 0 Customer Name: Great Gazoo Number of Books checked out: 2 Customer Name: Charlie Harper Number of Books checked out: 4 Customer Name: Neil Gruesome Number of Books checked out: 6

TIA the Catfish

Replies are listed 'Best First'.
Re: Format question
by tobyink (Canon) on Dec 03, 2019 at 16:15 UTC

    Look into using printf.

    printf "Customer Name: %-24s Number of Books checked out: % 2d\n", @it +ems[0,5];
Re: Format question
by hippo (Archbishop) on Dec 03, 2019 at 16:18 UTC

    An approach using format since that's in the title of your question.

    #!/usr/bin/env perl use strict; use warnings; my @input = split /\n/, <<EOT; wilma flinstone:3 barney rubble:5 Sam Slate:0 Great Gazoo:2 Charlie Harper:4 Neil Gruesome:6 EOT for my $line (@input) { my @fields = split /:/, $line; format = Customer Name: @<<<<<<<<<<<<<<<<<< Number of Books checked out: @< $fields[0], $field +s[1] . write; }

    Consider other options such as Template, printf, Text::Table, etc.

Re: Format question
by AnomalousMonk (Archbishop) on Dec 03, 2019 at 17:32 UTC

    WRT printf, see sprintf for a wealth of format specifiers.


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

Re: Format question
by roboticus (Chancellor) on Dec 03, 2019 at 21:11 UTC

    catfish1116:

    At first, I guessed that formatting problem is that the data lines contain whitespace which caused that first field to blow up. I was going to suggest that you tell split to ignore whitespace like the following:

    use strict; use warnings; my $line = <DATA>; while (defined($line = <DATA>)) { chomp($line); my @items = (split /\s*:\s*/, $line); print "Customer Name: $items[0] \t Number of Books checked out: +$items[5]\n"; } __DATA__ customer:field1:field2:field3:field4:num_checked_out wilma flintstone :1:m:n:o:3 barney rubble:1:e:i:o:5 Sam Slate :1:3:2:1:0 Great Gazoo:1:1:2:3:2 Charlie Harper:1:a:b:c:4 Neil Gruesome:1:x:y:z:6

    But when I ran the script, I got the same result you did:

    $ perl ex_split_ign_whitespace.pl Customer Name: wilma flintstone Number of Books checked out: + 3 Customer Name: barney rubble Number of Books checked out: 5 Customer Name: Sam Slate Number of Books checked out: 0 Customer Name: Great Gazoo Number of Books checked out: 2 Customer Name: Charlie Harper Number of Books checked out: 4 Customer Name: Neil Gruesome Number of Books checked out: 6

    That's when I looked at your print statement: You're using a tab to try to align the columns. But a tab simply tells the print statement to go to the next column that's a multiple of 8. When your input data has width differences, you'll find that a field that's a shade too long pushes everything over eight more characters.

    So the solution is to use printf with a width specifier (as tobyink mentioned), or the format statement (as hippo suggested) or pack or some such. I just tweaked it to use printf since it was the simplest change:

    use strict; use warnings; my $line = <DATA>; while (defined($line = <DATA>)) { chomp($line); my @items = (split /\s*:\s*/, $line); printf "Customer Name: %-16s Number of Books checked out:%3u\n" +, $items[0], $items[5]; } __DATA__ customer:field1:field2:field3:field4:num_checked_out wilma flintstone :1:m:n:o:3 barney rubble:1:e:i:o:5 Sam Slate :1:3:2:1:0 Great Gazoo:1:1:2:3:2 Charlie "the Angel" Harper:1:a:b:c:4 Neil Gruesome:1:x:y:z:1026

    In this version, I added a width specifier to both the customer name *and* the number of books checked out. Notice that the columns line up correctly *unless* one of the data items is too long to fit in the field (the last two lines).

    $ perl ex_split_ign_whitespace.pl Customer Name: wilma flintstone Number of Books checked out: 3 Customer Name: barney rubble Number of Books checked out: 5 Customer Name: Sam Slate Number of Books checked out: 0 Customer Name: Great Gazoo Number of Books checked out: 2 Customer Name: Charlie "the Angel" Harper Number of Books checked o +ut: 4 Customer Name: Neil Gruesome Number of Books checked out:1026
    One way you can handle that is to tell split that you don't care about whitespace around your delimiter, like this:

    that the data has a bunch of whitespace in it, causing yo

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Format question
by harangzsolt33 (Deacon) on Dec 04, 2019 at 15:02 UTC
    You could also capitalize the names in case they are all lower case.
    #!/usr/bin/perl use strict; use warnings; print ToTitleCase('barney rubble'); exit; ############################################ # v2019.12.4 # Capitalizes the first letter of every word, # and returns a new string. # Usage: STRING = ToTitleCase(STRING) # sub ToTitleCase { my $S = defined $_[0] ? lc($_[0]) : ''; my ($i, $p, $BREAK) = (-1, 0); while (++$i < length($S)) { ($BREAK = index(" .,:;!?/\\()[]{}<>|-+=\t\n\r\xFF", substr($S, $i, + 1))) >= 0 or $p < 0 or ($p = vec($S, $i, 8)) > 122 or vec($S, $i, 8) = $p & 223; $p = $BREAK; } return $S; } ############################################

      A slightly more Perlish alternative:

      #!/usr/bin/env perl use strict; use warnings; print title_case('barney rubble'); exit; sub title_case { $_ = shift; s/\b(\w)/\U$1/g; return $_; }

      See also the FAQ.

      Side note: Title case is an editorial term and procedure that is a bit more complicated than capitalizing words, the spelling of names does not have to constrain to any such rules, and uc/lc are not always uniform or roundtrippable operations outside of basic Roman letters.