perl

How can I print a table with multi-line strings using the Text::Table module?


I am using the CPAN Text::Table module. I have a table in my script and some values are multi-line strings. I am also using a rule to print this table.

My code is as follows:

#!/usr/bin/perl5.14.1
use FindBin;
use lib "$FindBin::Bin/mylib/Text-Aligner-0.12/lib/";
use lib "$FindBin::Bin/mylib/Text-Table-1.130/lib/";
use Text::Table;

my $tb = Text::Table->new(\'| ', "", \' | ', "Field ", \'| ', "Length ", \'| ', "Comment ", \' |');
my @AoA = (
  [ 1, "Foo", "20", "Foo" ],
  [ 2, "Bar", "35", "Bar\nBar" ],
  [ 3, "Tze", "10", "Tze\nTze" ],
);

$tb->load(@AoA);
my $rule = $tb->rule(qw/- /);
my @arr = $tb->body;


print $rule, $tb->title, $rule;
for (@arr) {
  print $_ . $rule;
}

However when I run this I get the following:

|---|-------|--------|----------|
|   | Field | Length | Comment  |
|---|-------|--------|----------|
| 1 | Foo   | 20     | Foo      |
|---|-------|--------|----------|
| 2 | Bar   | 35     | Bar      |
|---|-------|--------|----------|
|   |       |        | Bar      |
|---|-------|--------|----------|
| 3 | Tze   | 10     | Tze      |
|---|-------|--------|----------|
|   |       |        | Tze      |
|---|-------|--------|----------|

Is there a way to not print separate lines in the case of multi-line strings?

I want to display my table as follows:

|---|-------|--------|----------|
|   | Field | Length | Comment  |
|---|-------|--------|----------|
| 1 | Foo   | 20     | Foo      |
|---|-------|--------|----------|
| 2 | Bar   | 35     | Bar      |
|   |       |        | Bar      |
|---|-------|--------|----------|
| 3 | Tze   | 10     | Tze      |
|   |       |        | Tze      |
|---|-------|--------|----------|

Solution

  • The Text::Table code calls split( /\n/ ) on each row of your data and puts the results into their own rows. You can work around this by calculating which rows actually correspond to multi-line data and only printing a rule at the appropriate boundaries:

    use strict;
    use warnings;
    
    use List::Util qw(max);
    use Text::Table;
    
    my $sep = \'|';
    my @col_spec = ($sep, '', $sep, 'Field', $sep, 'Length', $sep, 'Comment', $sep);
    
    my $tb = Text::Table->new(@col_spec);
    
    my @data = (
        [ 1, "Foo", "20", "Foo" ],
        [ 2, "Bar\nBaz\nQux", "35", "Bar\nBar" ],
        [ 3, "Tze", "10", "Tze\nTze" ]
    );
    
    # Track which rows should be preceded by rules. A key of 'n' indicates that a
    # rule should be printed before the nth row (zero-indexed).
    my %indexes = (
        0 => 1, # Before header row
        1 => 1  # After header row
    );
    
    # Calculate body rows for which rules should be printed
    my $count = 1;
    foreach my $row (@data) {
        # Greatest number of newlines in row
        my $newlines = max map { tr/\n// } @$row;
    
        # One newline corresponds to two rows
        $count += $newlines + 1;
    
        $indexes{$count} = 1;
    }
    
    $tb->load(@data);
    
    my $rule = $tb->rule('-', '+');
    
    foreach my $i (0 .. $tb->height) {
        print $rule if exists $indexes{$i};
        print $tb->table($i);
    }
    

    Output:

    +-+-----+------+-------+
    | |Field|Length|Comment|
    +-+-----+------+-------+
    |1|Foo  |20    |Foo    |
    +-+-----+------+-------+
    |2|Bar  |35    |Bar    |
    | |Baz  |      |Bar    |
    | |Qux  |      |       |
    +-+-----+------+-------+
    |3|Tze  |10    |Tze    |
    | |     |      |Tze    |
    +-+-----+------+-------+