我有一个输入文件,如下所示
=IP1
abc[0]
abc[1]
abc[2]
=IP2
def[4]
def[8]
def[9]
我需要以下面的格式获得输出 -
=IP1
abc[0-2]
=IP2
def[4,8-9]
我一直在尝试使用哈希来实现上述操作,其中我读取文件的每一行然后拆分(使用' [')每一行,我将第一部分保留为键并再次读取文件将值保存在散列键的数组中。但我陷入了困境。任何人都可以提供如何实现上述的帮助?
答案 0 :(得分:1)
有几个有趣的子问题。首先,您要跟踪最新的标题(即=IP1
)。其次,您要跟踪与某些键关联的数字列表,第三,您想要生成范围字符串。
我将如何做到这一点:
#!/usr/bin/env perl
use strict;
use warnings;
my $tl;
my %h;
# First process the lines of the input file.
while(<DATA>) {
chomp;
next unless length;
if(/^(=\w{2}\d+)$/) { # Recognize and track a top level heading.
$tl = $1;
next;
}
if(/^(\w+)\[(\d+)\]$/) { # Or grab a key/value pair.
my($k,$v) = ($1,$2);
push @{$h{$tl}{$k}}, $v; # push the value into the right bucket.
next;
}
warn "Unrecognized format cannot be processed at $.: (($_))\n";
}
# Sort the top level headers alphabetically and numerically.
# Uses a Schwartzian Transform so that we don't need to recompute
# sort keys repeatedly.
my @topkeys = map {$_->[0]}
sort {$a->[1] cmp $b->[1] || $a->[2] <=> $b->[2]}
map {
my($alpha, $num) = $_ =~ m/^=(\w+)(\d+)$/;
[$_, $alpha, $num]
} keys %h;
# Now iterate through the structure in sorted order, generate range
# strings on the fly, and print our output.
foreach my $top (@topkeys) {
print "$top\n";
foreach my $k (sort keys %{$h{$top}}) {
my @vl = sort {$a <=> $b} @{$h{$top}{$k}};
my $range = num2range(@vl);
print "$k\[$range]\n";
}
}
sub num2range {
local $_ = join ',' => @_;
s/(?<!\d)(\d+)(?:,((??{$++1}))(?!\d))+/$1-$+/g;
return $_;
}
__DATA__
=IP1
abc[0]
abc[1]
abc[2]
=IP2
def[4]
def[8]
def[9]
生成以下输出:
=IP1
abc[0-2]
=IP2
def[4,8-9]
如果回答Borodin作为对原始帖子发表评论的一些问题的答案,则可以进一步优化此解决方案。例如,如果我们知道数字已经按顺序排列,则无需在生成范围之前对我们的数字列表进行排序。如果我们更多地了解“abc”和“def”是什么,那么可能会消除一些复杂性(和计算工作)。如果排序顺序无关紧要,我们可以进一步简化,同时减少正在完成的工作量。
此外,Set::IntSpan模块可能提供更强大的方法来生成范围字符串,并且如果此脚本旨在超出“一次性”生命周期,则可能值得考虑。如果您选择使用Set :: IntSpan,则num2range
sub可能如下所示:
sub num2range{ return Set::IntSpan->new(@_) }
Set::IntSpan
对象重载了字符串化,因此打印它会给出范围的文本表示。如果你选择这条路线,你可以删除对数字列表进行排序的代码 - 这是由Set :: IntSpan内部处理的。
答案 1 :(得分:1)
好的,这是我对解决方案的看法。没有关于传入数据的任何更好的信息,它可能比必要的更复杂
它保留数据 - =IP
标题和xyz[9]
值的顺序与首次遇到的顺序相同。我已经将数字范围收缩的生成分离到子例程ranges
只需将文件中的数据(它希望作为命令行上的参数)读入数据结构%data
和@order
并打印它们即可。再来一次。哈希的@order
数组和_order
子键用于保存遇到值的序列,并在将新键插入相应的哈希值时添加到该序列中
use strict;
use warnings;
my ($key, %data, @order);
while ( <> ) {
chomp;
if ( /^=/ ) {
$key = $_;
push @order, $key unless $data{$key};
$data{$key} = { _order => [] };
}
elsif ( my ($key2, $n) = /([^\[\]\s]+)\[(\d+)\]/ ) {
my $data = $data{$key};
push @{ $data->{_order} }, $key2 unless $data->{$key2};
push @{ $data->{$key2} }, $n;
}
}
for my $key ( @order ) {
print $key, "\n";
my $data = $data{$key};
for my $key2 ( @{ $data->{_order} } ) {
printf "%s[%s]\n", $key2, ranges( sort { $a <=> $b } @{ $data->{$key2} } );
}
}
sub ranges {
my @ranges;
my ($start, $end);
for my $n ( @_ ) {
if ( not defined $start ) {
$start = $end = $n;
}
elsif ( $n == $end + 1 ) {
$end = $n;
}
else {
push @ranges, $start == $end ? $start : "$start-$end";
$start = $end = $n;
}
}
push @ranges, $start == $end ? $start : "$start-$end" if defined $start;
join ',', @ranges;
}
=IP1
abc[0-2]
=IP2
def[4,8-9]