格式化为控制台网格/列的多维数组

时间:2018-04-30 03:54:06

标签: arrays perl formatting

使用Text :: Table或Text :: ANSITable,或类似的东西......

如果我有一个二维数组(表示数据网格),第一行可以是列标题,我该如何应用该数据并将其格式化为带有列的命令行网格。

像这样:PERL : How to create table from an array?

除了行数和列数根据数组设置而变化,并且需要自动输出。

由于

2 个答案:

答案 0 :(得分:1)

您可以使用Text::Table来完成此操作,因为它处理可变数量的行和列。虽然文档有很多不足之处,但您通常可以查看测试文件或示例,以向您展示库应该如何实际使用。我已经改编example.pl来说明这一点:

<强> table.pl

#!/usr/bin/perl

use strict;
use warnings;

use utf8;

use Text::Table;

binmode STDOUT, ':utf8';

my ($rows, $cols) = @ARGV;
$rows ||= 5;
$cols ||= 7;

my @cols = map { "column " . $_} ( 1..$cols );
my $sep = \'│';

my $major_sep = \'║';
my $tb = Text::Table->new($sep,  " Number ", $major_sep,
                          (map { +(" $_ ", $sep) } @cols)
    );

my $num_cols = @cols;

for my $row (1..$rows) {
    $tb->load([ "row $row", map { "r$row,c$_" } ( 1..$cols ) ]);
}

my $make_rule = sub {
    my ($args) = @_;

    my $left = $args->{left};
    my $right = $args->{right};
    my $main_left = $args->{main_left};
    my $middle = $args->{middle};

    return $tb->rule(
        sub {
            my ($index, $len) = @_;

            return ('─' x $len);
        },
        sub {
            my ($index, $len) = @_;

            my $char =
                (     ($index == 0) ? $left
                      : ($index == 1) ? $main_left
                      : ($index == $num_cols+1) ? $right
                      : $middle
                );

            return $char x $len;
        },
        );
};

my $start_rule = $make_rule->(
    {
        left => '┌',
        main_left => '╥',
        right => '┐',
        middle => '┬',
    }
    );

my $mid_rule = $make_rule->(
    {
        left => '├',
        main_left => '╫',
        right => '┤',
        middle => '┼',
    }
    );

my $end_rule = $make_rule->(
    {
        left => '└',
        main_left => '╨',
        right => '┘',
        middle => '┴',
    }
    );


print $start_rule, $tb->title,
    (map { $mid_rule, $_, } $tb->body()), $end_rule;

<强>输出

perl table.pl 3 5
┌────────╥──────────┬──────────┬──────────┬──────────┬──────────┐
│ Number ║ column 1 │ column 2 │ column 3 │ column 4 │ column 5 │
├────────╫──────────┼──────────┼──────────┼──────────┼──────────┤
│row 1   ║r1,c1     │r1,c2     │r1,c3     │r1,c4     │r1,c5     │
├────────╫──────────┼──────────┼──────────┼──────────┼──────────┤
│row 2   ║r2,c1     │r2,c2     │r2,c3     │r2,c4     │r2,c5     │
├────────╫──────────┼──────────┼──────────┼──────────┼──────────┤
│row 3   ║r3,c1     │r3,c2     │r3,c3     │r3,c4     │r3,c5     │
└────────╨──────────┴──────────┴──────────┴──────────┴──────────┘

答案 1 :(得分:0)

虽然模块提供更简单的控制和功能,但如果您只需要将其打印为网格

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

my @ary = ([1..3], [10..12], [100..102]);

foreach my $row (@ary) { 
    printf "%7.3f  ", $_ for @$row; 
    say ''
}

打印

  1.000    2.000    3.000  
 10.000   11.000   12.000  
100.000  101.000  102.000  

根据您拥有的数据选择您的说明符(上面%7.3f)。见sprintf

如果第一行是标题,shift它离开数组并以相同的宽度打印但使用%s

my @ary = ([qw(one two three)], [1..3], [10..12], [100..102]); 

printf "%7s  ", $_ for @{shift @ary};
say '';

foreach my $row (@ary) { 
    printf "%7.3f  ", $_ for @$row; 
    say ''
}

这打印与上面相同,但在第一行中有(对齐的)列名。

如果“数据网格”表示数字数据,则代码可以发现是否存在标题行,并合理地假设第一行包含某些内容非-numeric

use List::Util qw(any);

my $have_header = any { /[^0-9.+-]/ } @{$ary[0]};

还假设第一行没有NaN1e02等。

更好的是,使用Scalar::Util

中的looks_like_number
use List::Util qw(any);
use Scalar::Util qw(looks_like_number);

my $have_header = any { not looks_like_number($_) } @{$ary[0]};

这使用Perl的internal sense of what a number is,并考虑NaN和指数表示法等。

如果程序需要找出列的宽度,或者它们有很大差异,那么对于一个不错的输出还有很多工作要做,因为我们需要每列的最大宽度。

use warnings;
use strict;
use feature 'say';    
use List::Util qw(max);

my $file = shift @ARGV || 'data.txt';
open my $fh, '<', $file or die "Can't open $file: $!";
my @ary = map { [ split ] } <$fh>;

my @maxw = (1) x @{$ary[0]};   
for my $r (@ary) {
    for (0..$#$r) {
        my $len = length $r->[$_];
        $maxw[$_] = $len if $len > $maxw[$_]
    }
};

my $hdr = shift @ary;
printf "%$maxw[$_]s  ", $hdr->[$_] for 0..$#$hdr;
say '';

for my $i (0..$#ary) {
    printf "%$maxw[$_].2f  ", $ary[$i]->[$_] for 0..$#{$ary[$i]};
    say '';
}

这需要数字,但标题除外。还做了一些其他合理的假设。

使用输入data.txt文件填充数组

one      two      three  
1.12   1.1   12  
1.00  10.00  102.000

程序打印

 one    two    three  
1.12   1.10    12.00  
1.00  10.00   102.00  

请注意。如果在程序中计算了这些数字,那么这些标量将被用作数字,并且应该将用于打印它们的字符串的长度查询为length sprintf "%s", $num。当从文件中读取它们时,它们被视为字符串,为简单起见,上面使用的是什么。