tags:

views:

123

answers:

5

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 align 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?

+3  A: 

This answer is not mine, but 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.

Ashwin
Wow, people still use formats? How very perl 4 of you :)
Ether
+8  A: 

For formatting, try using the sprintf function, as in

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

(the minus sign means left-justify)

Jim Garrison
thank you! this is exactly what I was looking for.
qdog
A: 

Whatever you do, you're going to have to do with two passes - one to find the length, one to print, using that length in the formatting. I would go with the sprintf answer already given for the formatting, personally, just varying the number in it.

Exactly how you do the two passes rather depends on where the data is coming from - reading a file twice might be wiser than preprocessing it into memory if the file is huge, but the array solution you're proposing sounds good to me.

ijw
+1  A: 

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
Sinan Ünür
+2  A: 

Have a look at Perl6::Form.

This is the recommended approach over using format. See this previous SO 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).

Its easy to fix depending on what your output requirements are. For eg. if you want to maintain tabular stability then...

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

...will word wrap each column for you.

/I3az/

draegtun