Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "pad" a variable-length string to have an aligned last column

Tags:

perl

I have an input of the following format:


09:08:11        XXXXXXXXXXXXX  1.1.1.1          
09:09:03        YYYYYYYY  2.2.2.2         
09:12:37        ZZZZ   3.3.3.3

I am able to extract these individuals fields easily using the regex /(\S+)\s+(\S+)\s+(\S+)\s+/. I named them $time, $name, and $number. My problem is I want to display this so that the $number aligns perfectly. Because the $name can be of any length, what is the best solution for this?

I would like for the output to look like this. Please note that I had to use dots to align the last field since I wasn't able use the spacebar to do this, not sure why. Anyhoo.


09:08:11        XXXXXXXXXXXXX    1.1.1.1          
09:09:03        YYYYYYYY         2.2.2.2         
09:12:37        ZZZZ             3.3.3.3

I thought about putting the $name into an array. And then use a function to find the one with the longest character count. Finally I would pad out the shorter name to match the longest name. Is there a better and more efficient way to do this?

like image 254
qdog Avatar asked Oct 02 '09 23:10

qdog


4 Answers

For formatting, try using the sprintf function, as in

$line = sprintf "%-12s %-20s %-s\n", $time, $name, $number;

(the minus sign means left-justify)

like image 157
Jim Garrison Avatar answered Nov 17 '22 16:11

Jim Garrison


It was given by someone here:

my($name, $telephone, $stime);

format Infos =
@<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<
$name,             $telephone,         $stime
.

$~ = 'Infos';

($name, $telephone, $stime) = ('Name:', 'Telephone:', 'Start Time:');
write;

($name, $telephone, $stime) = ('Mike Bax', 'tel-41596', 'Fri 8/22 13:31');
write;

You can change variables names, and it should work for you.

like image 43
Ashwin Avatar answered Nov 17 '22 17:11

Ashwin


Have a look at Perl6::Form.

This is the recommended approach over using format. See this previous Stack Overflow answer: What other languages have features and/or libraries similar to Perl's format?

Here is an example of Perl6::Form doing just what you want with your data:

use Perl6::Form;

my ($time, $name, $number);

my @data = (
    [ qw(09:08:11 XXXXXXXXXXXXX 1.1.1.1) ],
    [ qw(09:09:03 YYYYYYYY 2.2.2.2)      ],
    [ qw(09:12:37 ZZZZ 3.3.3.3)          ],
);

for my $line (@data) {
    push @$time,   $line->[0];
    push @$name,   $line->[1];
    push @$number, $line->[2];
}

print form
    '{[[[[[[}    {[[[[[[[[[[[[[[[[[[[[}    {[[[[[[}',
    $time,       $name,                    $number;


NB. If your data fields ever become larger than the form fields specified then you will run into formatting issues (and same thing also applies to sprintf and format solutions).

It's easy to fix depending on what your output requirements are. For example, if you want to maintain tabular stability then...

print form { layout => 'tabular' },
    '{[[[[[[}    {[[[[[[[[[[[[[[[[[[[[}    {[[[[[[}',
    $time,       $name,                    $number;

...will word wrap each column for you.

like image 3
draegtun Avatar answered Nov 17 '22 15:11

draegtun


If you do not know the maximum width of the middle field, you will have to make two passes over the data as @ijw notes.

#!/usr/bin/perl

use strict;
use warnings;

use Fcntl qw( :seek );

my $max_length = 0;
my $data_pos = tell DATA;

while ( my $line = <DATA> ) {
    last unless $line =~ /\S/;
    my $name = (split ' ', $line)[1];
    my $name_length = length $name;
    $max_length = $name_length if $name_length > $max_length;
}

seek DATA, $data_pos, SEEK_SET;

while ( my $line = <DATA> ) {
    last unless $line =~ /\S/;
    my ($date, $name, $ip) = split ' ', $line;
    printf "%s %-${max_length}s %s\n", $date, $name, $ip;
}

__DATA__
09:08:11        XXXXXXXXXXXXX  1.1.1.1
09:09:03        YYYYYYYY  2.2.2.2
09:12:37        ZZZZ   3.3.3.3
like image 2
Sinan Ünür Avatar answered Nov 17 '22 15:11

Sinan Ünür