Many text files contain tabular data. Unfortunately, getting the columns to line up can prove difficult. For example, here is a typical file:
/dev/cdrom /mnt/cdrom iso9660 user,unhide,ro 0 0 /dev/scd1 /mnt/cdrom1 iso9660 noauto,unhide,user,ro 0 0 /dev/scd0 /mnt/cdrom2 iso9660 noauto,unhide,user,ro 0 0 /dev/loop0 /mnt/cdrom_l iso9660 noauto,unhide,user,ro 0 0 /dev/sda1 /mnt/optical ext2 defaults,noauto,user 0 0
It would be nice to see something like this:
/dev/cdrom /mnt/cdrom iso9660 user,unhide,ro 0 0 /dev/scd1 /mnt/cdrom1 iso9660 noauto,unhide,user,ro 0 0 /dev/scd0 /mnt/cdrom2 iso9660 noauto,unhide,user,ro 0 0 /dev/loop0 /mnt/cdrom_l iso9660 noauto,unhide,user,ro 0 0 /dev/sda1 /mnt/optical ext2 defaults,noauto,user 0 0
You can write a Perl program to process this text and make it look pretty. Start by reading in the data and dividing it up into words:
28 while (<>) { 29 chomp($_); 30 my @words = split /s+/, $_; 31 push (@lines, [@words]); 32 }
This example uses the magic Perl file <>, which goes through all the arguments on the command line. The result is an array called @lines in which each element is a line from the input.
The actual element is not stored as a string but rather as an array of words.
Next, this data structure is processed to determine which line has the most words on it:
37 my $n_fields = 0; 38 39 foreach my $cur_line (@lines) { 40 if ($#$cur_line > $n_fields) { 41 $n_fields = $#$cur_line; 42 } 43 }
Now you can go through and compute the size of each field:
48 my @field_size = (); 49 for (my $cur_field = 0; 50 $cur_field <= $n_fields; 51 $cur_field++) { 52 53 $field_size[$cur_field] = 0; 54 foreach my $cur_line (@lines) { 55 if (not defined($cur_line–>[$cur_field])) { 56 next; 57 } 58 if (length($cur_line–>[$cur_field]) > 59 $field_size[$cur_field]) { 60 $field_size[$cur_field] = 61 length($cur_line–>[$cur_field]); 62 } 63 } 64 }
You’re almost there. You need to create a format string that can be used for printing the table:
69 my $format = ""; 70 foreach my $cur_field (@field_size) { 71 if ($format ne "") { 72 $format .= " "; 73 } 74 $format .= "%–${cur_field}s"; 75 } 76 $format .= " ";
Finally, there’s nothing left to do but print it:
81 foreach my $cur_line (@lines) { 82 printf $format, @$cur_line; 83 }
The result is a beautifully formatted table. The entire program is presented in Listing 16.6.
1 =pod 2 3 =head1 NAME 4 5 table.pl – Print out tables nicely formatted 6 7 =head1 SYNOPSIS 8 9 table.pl <file> [<file> ....] 10 11 =head1 DESCRIPTION 12 13 The I<table.pl> examines the input files which are assumed to contain 14 data in columns and prints them nicely. In other words, it makes 15 the tables look nice. 16 17 =cut 18 use strict; 19 use warnings; 20 21 22 # The input as lines, then as columns 23 my @lines = (); 24 25 # 26 # Grab input, break apart into fields 27 # 28 while (<>) { 29 chomp($_); 30 my @words = split /s+/, $_; 31 push (@lines, [@words]); 32 } 33 34 # 35 # Find out how many fields there are 36 # 37 my $n_fields = 0; 38 39 foreach my $cur_line (@lines) { 40 if ($#$cur_line > $n_fields) { 41 $n_fields = $#$cur_line; 42 } 43 } 44 45 # 46 # Find the size of each field. 47 # 48 my @field_size = (); 49 for (my $cur_field = 0; 50 $cur_field <= $n_fields; 51 $cur_field++) { 52 53 $field_size[$cur_field] = 0; 54 foreach my $cur_line (@lines) { 55 if (not defined($cur_line–>[$cur_field])) { 56 next; 57 } 58 if (length($cur_line–>[$cur_field]) > 59 $field_size[$cur_field]) { 60 $field_size[$cur_field] = 61 length($cur_line–>[$cur_field]); 62 } 63 } 64 } 65 66 # 67 # Build the printing format 68 # 69 my $format = ""; 70 foreach my $cur_field (@field_size) { 71 if ($format ne "") { 72 $format .= " "; 73 } 74 $format .= "%–${cur_field}s"; 75 } 76 $format .= " "; 77 78 # 79 # Print 80 # 81 foreach my $cur_line (@lines) { 82 printf $format, @$cur_line; 83 } 84 |