使用Text :: Table或Text :: ANSITable,或类似的东西......
如果我有一个二维数组(表示数据网格),第一行可以是列标题,我该如何应用该数据并将其格式化为带有列的命令行网格。
像这样:PERL : How to create table from an array?
除了行数和列数根据数组设置而变化,并且需要自动输出。
由于
答案 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]};
还假设第一行没有NaN
和1e02
等。
更好的是,使用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
。当从文件中读取它们时,它们被视为字符串,为简单起见,上面使用的是什么。