File Coverage

blib/lib/Text/Tabulate.pm
Criterion Covered Total %
statement 203 223 91.0
branch 60 80 75.0
condition 14 22 63.6
subroutine 16 19 84.2
pod 6 8 75.0
total 299 352 84.9


"; " . $tab->{gutter} . "
line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2              
3             =head1 NAME
4              
5             Text::Tabulate - a pretty text data tabulator that minimises the width of tables.
6              
7             =head1 SYNOPSIS
8              
9             use Text::Tabulate;
10              
11             $tab = new Text::Tabulate ();
12              
13             $tab->configure();
14              
15             @out = $tab->format(@lines);
16              
17             @out = $tab->common(@lines);
18              
19              
20             @out = tabulate ( { tab => '???', ...}, @lines);
21              
22             @out = tabulate ( $tab, $pad, $gutter, $adjust, @lines);
23              
24             =head1 DESCRIPTION
25              
26             This perl module takes an array of line text data, each line separated
27             by some string matching a given regular expression, and returns a
28             minimal width text table with each column aligned.
29              
30             =head1 FUNCTIONS
31              
32             =over 4
33              
34             =cut
35              
36              
37             ;#####################################################################################
38              
39             package Text::Tabulate;
40              
41 5     5   169682 use 5.006_001;
  5         19  
  5         259  
42 5     5   108 use warnings;
  5         10  
  5         180  
43 5     5   27 use strict;
  5         25  
  5         193  
44 5     5   29 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  5         9  
  5         528  
45 5     5   29 use Carp;
  5         13  
  5         32018  
46              
47             require Exporter;
48              
49             $VERSION = '1.1.1';
50             @ISA = qw(Exporter);
51             @EXPORT = qw( tabulate );
52             @EXPORT_OK = ();
53              
54 672     672 0 988 sub debug {}
55              
56             ;# Default tabulate settings.
57             my %defaults = (
58             tab => "\t",
59             eol => '(\n)|(\r\n)|(\r)',
60             pad => ' ',
61             gutter => ' ',
62             adjust => '',
63             ignore => undef,
64             cf => -1,
65             ditto => '',
66             left => '',
67             right => '',
68             bottom => '',
69             top => '',
70             joint => '',
71             );
72              
73             =pod
74              
75             =item C
76              
77             my $tab = new Text::Tabulate( -tab => 'tab', ...);
78              
79             Create an Text::Tabulate object.
80             All CONFIGURATION OPTIONS are accepted, with or without a leading -.
81              
82             =cut
83              
84             ;# NB allow this: my $a = $b->new();
85              
86             sub new
87             {
88             # Create an object.
89 20     20 1 17222 my $this = shift;
90 20   33     143 my $class = ref($this) || $this;
91 20         41 my $self = { };
92 20         51 bless $self, $class;
93              
94             # Initialise
95 20         63 $self->initialise();
96              
97             # Load args into $self.
98 20 50       78 unless ($self->configure(@_))
99             {
100 0         0 croak "$class: initialisation failed!";
101 0         0 return undef;
102             }
103              
104             #use Data::Dumper; warn Dumper(\$self);
105              
106 18         53 $self;
107             }
108              
109             ;# "private" function.
110             sub initialise
111             {
112 20     20 0 46 my ($self) = @_;
113              
114             # Load defaults
115 20         107 while ( my ($k, $v) = each %defaults)
116             {
117 260         889 $self->{$k} = $v;
118             }
119              
120             # return object
121 20         38 $self;
122             }
123              
124              
125             =pod
126              
127             =item C
128              
129             my $tab = new Text::Tabulate();
130             $tab->configure(-tab => 'tab', gutter => '|', ...);
131              
132             This function chages the configuration settings of a Text::Tablulate
133             object.
134             All CONFIGURATION OPTIONS are accepted, with or without a leading -.
135              
136             =cut
137              
138             sub configure
139             {
140 32     32 1 69 my $self = shift;
141              
142 32 100       114 unless ($#_ % 2)
143             {
144 1         176 croak ref($self), ": Odd number of arguments";
145 0         0 return 0;
146             }
147              
148             # Load args into $self.
149 31         116 my %arg = @_;
150 31         112 while ( my ($k, $v) = each %arg)
151             {
152             # Remove any leading -
153 100         120 my $kk = $k; $kk =~ s/^-//;
  100         157  
154              
155             # Is this a real config option?
156 100 100       206 next unless exists $defaults{$kk};
157              
158             # Set option.
159 98         369 $self->{$kk} = delete $arg{$k};
160             }
161              
162             # Are there extra options?
163 31 100       78 if (%arg)
164             {
165 2         9 my @extras = sort keys %arg;
166 2 50       8 my $s = ($#extras > 0) ? 's' : '';
167 2         478 croak ref($self), ": Extra configuration option$s '", join("', '", @extras), "'";
168 0         0 return 0;
169             }
170              
171 29         83 $self;
172             }
173              
174             =pod
175              
176             =item C
177              
178             my $tab = new Text::Tabulate(...);
179             @out = $tab->format (@lines);
180              
181             Format the table data (@lines) according to the Text::Tabulate object.
182              
183             =cut
184              
185             sub format
186             {
187 11     11 1 56 my ($self, @lines) = @_;
188              
189 11         27 my $tab = $self->{tab};
190 11         19 my $eol = $self->{eol};
191 11         18 my $pad = $self->{pad};
192 11         22 my $gutter = $self->{gutter};
193 11         41 my $adjust = $self->{adjust};
194 11         21 my $ignore = $self->{ignore};
195 11         17 my $left = $self->{left};
196 11         16 my $right = $self->{right};
197 11         19 my $cf = $self->{cf};
198 11         19 my $ditto = $self->{ditto};
199 11         35 my $bottom = $self->{bottom};
200 11         18 my $top = $self->{top};
201 11         16 my $joint = $self->{joint};
202              
203             # Repackage lines, split with eol regular expression
204             # remembering the end of line string.
205 11         21 my @l = ();
206 11         17 my @eol = ();
207 11         34 for my $line (@lines)
208             {
209             # Split into lines...
210 86         1365 while ($line =~ s/^(.*?)($eol)//s)
211             {
212 83         217 push @l, $1;
213 83         1293 push @eol, $2;
214              
215             }
216              
217             # If there is any left, just add.
218 86 100       154 if ($line)
219             {
220 85         100 push @l, $line;
221 85         127 push @eol, '';
222             }
223             }
224 11         51 @lines = @l;
225              
226             # ignore blank lines at end.
227 11         18 my @blanks = ();
228 11   33     110 while (@lines && $lines[$#lines] =~ /^\s*$/)
229             {
230 0         0 push @blanks, pop(@lines);
231             }
232              
233             # Remove common first column entries?
234 11 50       33 @lines = $self->common(@lines) if ($cf >= 0);
235              
236 11         14 local ($_);
237              
238             # extract the maximum column widths.
239 11         12 my @width;
240 11         22 my $cols = 0;
241 11         769 for my $line (@lines)
242             {
243             # ignore line like the $ignore regular expression.
244 168 50 33     371 next if (defined($ignore) && ($line =~ /$ignore/));
245              
246             # Look through the fields.
247 168         176 my $i = 0;
248 168         682 my @cell = split(/$tab/, $line);
249 168 100       394 $cols = $#cell if ($#cell > $cols);
250              
251 168         249 for (@cell)
252             {
253 958         993 my ($l) = length;
254              
255 958 100 100     3370 $width[$i] = $l if (!defined($width[$i]) || $width[$i] < $l);
256              
257 958         1790 ++$i;
258             }
259              
260 168         642 debug "checking widths: $line\n";
261 168         499 debug " widths: " , join(", ", @width) , "\n";
262             }
263              
264 11         32 my @adjust = split(//, $adjust);
265              
266             # extend padding if needs be.
267 11 50       41 $pad = ' ' if (length($pad) < 1);
268 11 100       32 if (length($pad) == 1)
269             {
270 7         21 $pad .= ${pad}x(2+$cols);
271             }
272 11         44 my @pad = split(//, $pad);
273              
274 11         17 my @table;
275              
276             # add top.
277 11 100       28 if ($top)
278             {
279 2         6 my $out = '';
280 2 50       7 if (length($top) == 1)
281             {
282 2         6 $top .= ${top}x(2+$cols);
283             }
284 2         10 my @top = split(//, $top);
285              
286 2         11 for (my $i=0; $i<=$#width; $i++)
287             {
288 14 100       30 $out .= $gutter if ($i);
289 14         40 $out .= $top[$i]x$width[$i];
290             }
291              
292 2         8 push @table, $left . $out . $right;
293             }
294              
295             # recontruct each line with the correct padding and spacing.
296 11         21 for my $line (@lines)
297             {
298             # ignore line like the $ignore regular expression.
299 168 50 33     1098 if (defined($ignore) && ($line =~ /$ignore/))
300             {
301 0         0 push (@table, $line);
302 0         0 next;
303             }
304              
305 168         508 debug "recontructing: '$line'\n";
306              
307 168         256 my $i = 0;
308 168         182 my $out = '';
309              
310             # remove any end of line characters.
311 168 50       568 my $end = ($line =~ s/[\r\n]+$//) ? $& : '';
312              
313             # Go through the columns and pad, adjust, etc..
314 168         719 my @cell = split(/$tab/, $line);
315 168         557 while ($#cell < $cols)
316             {
317 194         400 push @cell, '';
318             }
319              
320 168         233 for (@cell)
321             {
322 1152         1514 my $l = $width[$i] - length;
323              
324             # Default for justification.
325 1152 100       2784 $adjust[$i] = 'l' unless (defined $adjust[$i]);
326              
327             # how to adjust in the column.
328             # The default is to left adjust.
329 1152 100       2543 my $f = ($adjust[$i] eq 'r') ? $l :
    100          
330             ( ($adjust[$i] eq 'c') ? int($l/2) :
331             0 );
332 1152         1178 my $b = $l - $f;
333              
334 1152         11384 my $fpad = $pad[$i];
335 1152         1345 my $bpad = $pad[$i+1];
336              
337 1152   100     2361 $fpad ||= ' ';
338 1152   100     2351 $bpad ||= ' ';
339              
340             # gutter and adjust.
341 1152 100       2064 $out .= $gutter if ($i > 0);
342 1152 100       2335 $out .= ${fpad}x$f if ($f>0);
343 1152         1176 $out .= $_;
344 1152 100       2280 $out .= ${bpad}x$b if ($b>0);
345              
346             # next column please.
347 1152         2162 ++$i;
348             }
349              
350             #print "I: $line";
351             #print "O: $out";
352              
353 168         589 debug "becomes : '$out'\n";
354              
355             # reassemble
356 168         447 push (@table, $left . $out . $right . $end);
357              
358             # extend eol array; use last value.
359 168         482 unshift @eol, $eol[0];
360             }
361              
362             # add bottom.
363 11 100       30 if ($bottom)
364             {
365 2         7 my $out = '';
366 2 50       8 if (length($bottom) == 1)
367             {
368 2         5 $bottom .= ${bottom}x(2+$cols);
369             }
370 2         21 my @bottom = split(//, $bottom);
371              
372 2         28 for (my $i=0; $i<=$#width; $i++)
373             {
374 14 100       26 $out .= $gutter if ($i);
375 14         35 $out .= $bottom[$i]x$width[$i];
376             }
377              
378 2         6 push @table, $left . $out . $right;
379              
380             # extend eol array; use last value.
381 2         6 push @eol, $eol[$#eol];
382             }
383              
384             # add the blank lines.
385 11         17 push @table, @blanks;
386              
387             # strip any white space at the end of the lines.
388 11         21 for (@table) { s/[\t ]+$//; }
  172         565  
389              
390             # Rejoin the eol of table line strings.
391 11         29 for my $line (@table)
392             {
393 172         229 $line .= shift(@eol);
394             }
395              
396             # return final table.
397 11 50       1029 return @table if (wantarray);
398              
399             # combine the array into a single string.
400 0         0 join($joint, @table);
401             }
402              
403             =pod
404              
405             =item C
406              
407             my $tab = new Text::Tabulate();
408             $tab->configure(-tab => 'tab', cf => 2, ditto => '?');
409             @out = $tab->common(@lines);
410              
411             This function returns an array of lines identical to the input except
412             that any repeated common value in the first column is removed in
413             subsequent lines and replaced by the string $ditto. If $max is positive,
414             then only that number of columns are considered; otherwise all column
415             are considered.
416              
417             The array of lines, @lines, is assumed to be an array of sigle table
418             rows.
419              
420             =cut
421              
422             ;# Take an 'tab' string and a array of lines and return the array with
423             ;# any repeated first column values obmitted.
424             sub common
425             {
426 5     5 1 32 my ($self, @lines) = @_;
427              
428 5         13 my ($tab) = $self->{tab};
429 5         9 my ($max) = $self->{cf};
430 5         10 my ($ditto) = $self->{ditto};
431              
432 5         7 local ($_);
433              
434             # look through all the lines....
435 5         9 my (@last);
436 5         10 for (@lines)
437             {
438             # ignore if there is no tab.
439 25 50       185 next unless /$tab/;
440              
441             # Split the line into cells.
442 25         147 my @this = split(/$tab/, $_);
443              
444             # look at each line.
445 25 100       72 if (@last)
446             {
447             # consider this line against the last.
448 20         55 my $tmp = '';
449 20         24 my $i = 0;
450 20         23 while (1)
451             {
452 46 100 100     172 last if ($max > 0 && $i >= $max);
453 40 50       75 last unless defined($this[$i]);
454 40 50       90 last unless defined($last[$i]);
455 40 100       82 last unless ($this[$i] eq $last[$i]);
456            
457 26         30 $i++;
458              
459             # Remove field.
460 26         135 s/.+?($tab)?//;
461              
462             # Remember duplicate fields.
463 26         38 $tmp .= $ditto;
464 26         51 $tmp .= $1;
465             }
466              
467             # reassemble line.
468 20         42 $_ = $tmp . $_;
469             }
470              
471             # Remember the last line.
472 25         96 @last = @this;
473             }
474              
475             # return ammended table.
476 5         27 @lines;
477             }
478              
479             ;#############################################################################
480              
481             =pod
482              
483             =item C
484              
485             @out = tabulate ( { tab => '???', ...}, @lines);
486             @out = tabulate ( $tab, $pad, $gutter, $adjust, @lines);
487              
488             This function returns an array of formated lines identical to the input except
489             that tab separated columns have been aligned with the padding chacater.
490              
491             It can be involked in two ways; either with an hashed array of arguments
492             followed by an array of lines or by 4 parameters (tab, pad, gutter,
493             adjust) followed by an array of lines.
494              
495              
496             Suggested usage:
497              
498             perl -MText::Tablutate -e'tabulate {gutter=>"|",}'
499              
500             =cut
501              
502             sub tabulate
503             {
504 5     5 1 12024 my $obj = new Text::Tabulate();
505              
506             # array version
507 5 50       25 if (ref $_[0] eq '')
    50          
508             {
509 0         0 $obj->configure(
510             tab => shift,
511             pad => shift,
512             gutter => shift,
513             adjust => shift,
514             );
515             }
516              
517             # hash version.
518             elsif (ref $_[0] eq 'HASH')
519             {
520 5         9 $obj->configure( %{$_[0]});
  5         23  
521 5         12 shift;
522             }
523              
524             # Wrong arguments.
525             else
526             {
527 0         0 croak ref($obj), "; tabulate error!";
528             }
529              
530 5         21 $obj->format(@_);
531             }
532              
533              
534             =pod
535              
536             =item C
537              
538             Text::Tabulate::filter(@ARGV)
539              
540             Act as a UNIX filter taking input from either STDIN or files specified
541             as function arguments, and sending the resulting formtted table to STDOUT.
542             Additional arguments will modify the behavour.
543              
544             perl -MText::Tablutate -e'filter(@ARGV)' [files]
545              
546             This function is involked if the Text::Tabulate module is run as a perl script.
547              
548             perl Text/Tabulate.pm [input-files]
549              
550             The function options are
551              
552             =over 4
553              
554             =item -s|--stanza
555              
556             Treat each paragraph as a individual table.
557              
558             =item -h|--html
559              
560             Format each table as HTML.
561              
562             =back
563              
564             The other options correspond to the configuration options of the
565             module.
566              
567             =over 4
568              
569             =item -t|--tab
570              
571             Set the tab string.
572             See module configuation options.
573              
574             =item -e|--eol
575              
576             Set the regular expression denoting an end of a table row.
577             See module configuation options.
578              
579             =item -p|--pad
580              
581             Set the pad character.
582             See module configuation options.
583              
584             =item -g|--gutter
585              
586             Set the gutter string.
587             See module configuation options.
588              
589             =item -I|--Ignore
590              
591             Ignore lines that match this regular expression.
592             See module configuation options.
593              
594             =item -a|--adjust
595              
596             Justify columns according to this string.
597             See module configuation options.
598              
599             =item -T|--top
600              
601             Set the top border characters.
602             See module configuation options.
603              
604             =item -B|--top
605              
606             Set the bottom border characters.
607             See module configuation options.
608              
609             =item -l|--left
610              
611             Set the left border string.
612             See module configuation options.
613              
614             =item -r|--right
615              
616             Set the right border string.
617             See module configuation options.
618              
619             =item -c|--cf
620              
621             This specifies if repeated values in the first few fields should be
622             replaced by the empty string.
623             See module configuation options.
624              
625             =item -d|--ditto
626              
627             This specified the string that replaces common values (see cf above).
628             See module configuation options.
629              
630             =back
631              
632             =cut
633              
634             sub filter
635             {
636             # Load these modules if we are running this function.
637             # Exit gracefully if we can't.
638 1 50   1 1 65 our @missing = grep( !eval "use $_; 1", qw (
  1     1   6  
  1     1   3  
  1         67  
  1         1230  
  1         20020  
  1         6  
639             File::Basename
640             Getopt::Long
641             )) and die "Please install CPAN modules:\n\tcpan -i @missing\n";
642              
643             # Initialise.
644 1         7 my $tab = new Text::Tabulate();
645 1         2 my $bystanza = 0;
646 1         2 my $html = 0;
647 1         61 my $program = basename($0);
648              
649             # usage
650 1         5 my $usage =
651             "Usage:\t$program --usage
652             $program []
653            
654             Options:
655             -p|--pad set the pad character
656             -t|--tab set the tab string; default is
657             -e|--eol set the eol regular expression
658             -g|--gutter set the gutter
659             -I|--Ignore ignore lines that match this reg-ex
660             -a|--adjust justify columns as this string
661             -c|--cf set the number of common valued cells to remove.
662             -d|--ditto set the dulpicate value replacement string.
663             -T|--top set the top border
664             -B|--bottom set the bottom border
665             -r|--right set the right border
666             -l|--left set the left border
667              
668             -s|--stanza treat each paragraph as a individual table
669             -h|--html output an HTML table
670             ";
671              
672             ################# start of command processing. #################
673              
674             # Use a local copy for this function.
675 1         6 local @ARGV = @_;
676              
677             # Load all the default options as flags.
678 1         2 my %opts = ();
679 1         4 for my $opt (keys %defaults)
680             {
681 13         33 $opts{"$opt=s"} = \$tab->{$opt};
682             }
683              
684 1         4 &Getopt::Long::config(qw(bundling auto_abbrev require_order));
685             GetOptions(
686 0     0   0 'usage' => sub { print $usage; exit; },
  0         0  
687              
688             # From module defaults
689             %opts,
690              
691             # aliases.
692             'p=s' => \$tab->{pad},
693             't=s' => \$tab->{tab},
694             'e=s' => \$tab->{eol},
695             'i=s' => \$tab->{ignore},
696             'g=s' => \$tab->{gutter},
697             'a=s' => \$tab->{adjust},
698             'c=i' => \$tab->{cf},
699             'l=s' => \$tab->{left},
700             'r=s' => \$tab->{right},
701             'T=s' => \$tab->{top},
702             'B=s' => \$tab->{bottom},
703              
704             # Extras
705             's|stanza+' => \$bystanza,
706             'h|html+' => \$html,
707 0     0   0 'v|version' => sub { print "$VERSION\n"; exit; },
  0         0  
708              
709             'debug' => sub {
710 5     5   55 no warnings;
  5         11  
  5         2050  
711 0     0   0 eval 'sub debug { print STDERR @_; }';
712             },
713 1 50       61 ) || die $usage;
714              
715              
716             ################## rest of the script goes here. #################
717              
718 1         1980 my $startTab = '';
719 1         2 my $startRow = '';
720 1         3 my $endRow = "\n";
721 1         2 my $endTab = '';
722              
723 1 50       4 if ($html%2)
724             {
725 0         0 $startTab = "\n";
726 0         0 $endTab = "
\n";
727              
728 0         0 $tab->{left} = "
" .$tab->{left};
729 0         0 $tab->{right} .= "
730              
731 0         0 $tab->{gutter} = "";
732             }
733              
734             # slurp or stanza mode?
735 1         3 $bystanza = ($bystanza%2);
736 1 50       7 local $/ = $bystanza ? '' : undef;
737              
738             # read in the data.
739 1         94 while (<>)
740             {
741 1         6 my @table = $tab->format($_);
742              
743 1 50       4 next unless (@table);
744              
745 1         3 print $startTab;
746 1         802 print join('', @table);
747 1         50 print $endTab;
748             }
749             }
750              
751             ;# self run
752             filter(@ARGV) if ($0 eq __FILE__);
753              
754             =pod
755              
756             =head1 CONFIGURATION OPTIONS
757              
758             The module configuration options are:
759              
760             =over 4
761              
762             =item tab
763              
764             This specified a regular expression denoting the original table
765             separator. The default is .
766              
767             =item eol
768              
769             This specified a regular expression denoting the end of table lines.
770             The default is '(\n)|(\r\n)|(\r)' to match most text formats. These
771             end of line strings are replaced after the table is formating.
772              
773             =item pad
774              
775             This specified the character used to pad the fields in the final
776             representation of the table. The default is a space.
777              
778             =item gutter
779              
780             This specifies the string places between columns in the final
781             representation. The default is the empty string.
782              
783             =item adjust
784              
785             This is a string specifying the justification of each field in the final
786             representation. Each character of this string should be 'r', 'l' or 'c'.
787             The default is left justification for all fields.
788              
789             =item ignore
790              
791             This regular expresion specifies lines that should be ignored. The
792             default is not to ignore any line.
793              
794             =item cf
795              
796             This specifies if repeated values in the first few fields should be
797             replaced by the empty string. The default is not to do this.
798              
799             =item ditto
800              
801             This specified the string that replaces common values (see cf above).
802              
803             =item top
804              
805             This specified the characters to be placed at the top of the table as a
806             border. If it is one character, then this is used as every character on
807             the top border. If there are more than one character then the first is
808             used for the first column, the second character for the second column,
809             etc.. The default is empty (i.e. no top border).
810              
811             =item bottom
812              
813             This specified the characters to be placed at the bottom of the table as a
814             border. If it is one character, then this is used as every character on
815             the bottom border. If there are more than one character then the first is
816             used for the first column, the second character for the second column,
817             etc.. The default is empty (i.e. no bottom border).
818              
819             =item left
820              
821             This specifies the strings to be placed as a border on the left of the
822             table. The default is nothing.
823              
824             =item right
825              
826             This specifies the strings to be placed as a border on the right of the
827             table. The default is nothing.
828              
829             =item joint
830              
831             This specifies the string used to join the rows of the table when the
832             I and I functions are called in a scalar context.
833             This is most useful when the table input is split on newlines and
834             a scaler return is required that includes newlines. Very similar to
835             I but depends on the context.
836             The default is nothing.
837              
838             =back
839              
840             =cut
841              
842             =pod
843              
844             =back
845              
846             =head1 EXAMPLE
847              
848             use Text::Tabulate;
849             my $tab = new Text::Tabulate();
850             $tab->configure(-tab => "\t", gutter => '|');
851              
852             my @lines = <>:
853             @out = $tab->format (@lines);
854             print @out;
855              
856             =head1 VERSION
857              
858             This is version 1.0 of Text::Tabulate, released 1 July 2007.
859              
860             =head1 AUTHOR
861              
862             Anthony Fletcher
863              
864             =head1 COPYRIGHT
865              
866             Copyright (c) 1998-2007 Anthony Fletcher. All rights reserved.
867             This module is free software; you can redistribute them and/or modify
868             them under the same terms as Perl itself.
869              
870             This code is supplied as-is - use at your own risk.
871              
872             =cut
873              
874             1;
875