4

I am reading from a file and again writing to another file. So when writing due to some big words alignment of the subsequent column gets distorted. I want to align all columns regardless of their length. I got one related question here. But the problem is that they are using Perl module Perl6::Form, and I want to do without any modules.

#This the input file
Hey! How Are You
I   AM  FINEEE Thankyouuu
1   22   333   4444

After alignment it should look like this:

Hey!  How   Are     You
I     AM    FINEEE  Thankyouuu
1     22    333     4444

Code tried (UPDATED):

#!usr/bin/perl
use warnings;
use strict;
use feature 'say';
$file1 = "file1.log";
$temp = "temp.log";
open(OUT, "<$file1") or die "Could not open file $file1: $!";
open(temp,"+>>$temp") or die "Could not open file $temp: $!";

while (my $line = <OUT>) {
    my @fs = split " ", $line;   
     
my @rows = @fs ;
 

@col_lens =  map { length } @rows if $.==1;

   for my $col_idx (0..$#rows) {
      my $col_len = length $rows[$col_idx];
      if ($col_lens[$col_idx] < $col_len) {                  
         $col_lens[$col_idx] = $col_len;               #line 144
      }
   }
   
     
     say temp1 (join "|",@fs);
 }
 
    close temp1;
    close OUT;

Update: The code is working and giving me the length of biggest word, but getting a few warnings also. But now how will I add spaces to each column according to max size of word in it? say temp1 (join "|",@fs); this only add "|" in between them.

Use of uninitialized value in numeric lt (<) at log.pl line 144,
Use of uninitialized value in numeric lt (<) at log.pl line 144,
Use of uninitialized value in numeric lt (<) at log.pl line 144,
Use of uninitialized value in numeric lt (<) at log.pl line 144,
Use of uninitialized value in numeric lt (<) at log.pl line 144,
Use of uninitialized value in numeric lt (<) at log.pl line 144,
Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47
Alex
  • 59
  • 4
  • Why don't you use tabs? They will not align the text in a text editor, but they will make it machine-readable and openable by *Excel*. – Alexander Mashin Oct 01 '20 at 05:07
  • If you're not limited to perl, you can consider using `column -t `. – Light Oct 01 '20 at 05:14
  • 1
    @AlexanderMashin, Yes using tab then generate a tab limited xls is good option . But I have again generate log format so that's why needed alignment . – Alex Oct 01 '20 at 05:18
  • `my @col_lens = map { length } @rows;` should get you current line column lengths. Find maximum values for each column, seek to beginning of file, and printf() to output file using maximum column widths. – mpapec Oct 01 '20 at 06:45
  • @Сухой27 , Can you check the updated part? Thankyou. – Alex Oct 01 '20 at 08:02
  • Confused me a little that you named the file handle OUT when this is a file you read in. Also confusing is two arrays for the same thing: `my @rows = @fs ;` – Kjetil S. Oct 01 '20 at 08:55
  • `printf` can handle tasks like this – TLP Oct 01 '20 at 12:56
  • Is there a reason you cannot just assume that for example 20 width is enough? – TLP Oct 01 '20 at 15:52

3 Answers3

1

Perhaps something of following kind should do the job

use strict;
use warnings;
use feature 'say';

my @lines;
my @max_len;

while( <> ) {
    next if /^#/;
    chomp;
    my @data = split;
    push @lines, \@data;
    my @length = map { length } @data;
    @max_len = map { $max_len[$_] || $length[$_] } 0..$#length;
    @max_len = map { $max_len[$_] < $length[$_] ? $length[$_] : $max_len[$_] } 0..$#length;
}

my @formats = map { '%-'. $_ . 's' } @max_len;
my $format  = join ' ', @formats, "\n";

printf $format, @{$_} for @lines;

Run as script.pl file1.log produces following output

Hey! How Are    You
I    AM  FINEEE Thankyouuu
1    22  333    4444

NOTE: output can be redirected into output file script.pl file1.log > temp.log

Polar Bear
  • 6,762
  • 1
  • 5
  • 12
1

Put this in the file program.pl

#!/usr/bin/perl
use warnings; use strict; my(@table,@len);
while(<>){
    my @row = split /\s+/;
    for my $col ( 0 .. $#row ){
        my $l = length($row[$col]);
        $len[$col] = $l if not defined$len[$col] or $l > $len[$col];
    }
    push @table, \@row;
}
my $format = join "  ", map '%-'.$_.'s', @len;
printf "$format\n", @$_ for @table;

...and run:

perl program.pl inputfile.txt > outputfile.txt

or perhaps

chmod +x program.pl
cat inputfile.txt | ./program.pl > outputfile.txt
Kjetil S.
  • 3,468
  • 20
  • 22
1

Do not reinvent the wheel. The *NIX utility column handles this task well (see also the comment by Light).

Examples:

Split input on whitespace (tabs, blanks, etc):

perl -le 'print join "\t", qw(Hey! How Are You); print join "\t", qw(I AM FINEEE Thankyouuu ), q{John Doe};' | \
  column -t 
Hey!  How  Are     You
I     AM   FINEEE  Thankyouuu  John  Doe

Here, John Doe is treated as 2 fields.


Split input on tabs (treat fields with blanks as single fields):

perl -le 'print join "\t", qw(Hey! How Are You); print join "\t", qw(I AM FINEEE Thankyouuu ), q{John Doe};' | \
  column -t -s$'\t'
Hey!  How  Are     You
I     AM   FINEEE  Thankyouuu  John Doe

Here, John Doe is treated as 1 field.

Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47