我有一个输入文件,如下所示
=IP1
abc[0]
abc[1]
abc[2]
=IP2
def[4]
def[8]
def[9]我需要获得以下格式的输出-
=IP1
abc[0-2]
=IP2
def[4,8-9]我一直在尝试使用散列来实现上述目的,我读取文件的每一行,然后拆分(用‘[’)每一行,我保留第一部分作为键,并再次读取文件以将值保存在散列键的数组中。但是我陷入了一个循环中。有谁能提供如何实现上述目标的帮助吗?
发布于 2015-08-03 13:03:44
有几个有趣的子问题。首先,您希望跟踪最新的头文件(即=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 num2range{ return Set::IntSpan->new(@_) }Set::IntSpan对象重载了字符串化,因此打印它会给出范围的文本表示形式。如果采用这种方式,就可以省去对数字列表进行排序的代码--这是由Set::IntSpan内部处理的。
发布于 2015-08-03 16:57:22
好吧,这是我对解决方案的看法。如果没有关于传入数据的任何更好的信息,它可能会比必要的更复杂
它保持数据--包括=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]https://stackoverflow.com/questions/31779141
复制相似问题