【问题标题】:Perl Scripting | using HASHesPerl 脚本 |使用哈希
【发布时间】:2015-10-25 01:53:47
【问题描述】:

我有一个如下所示的输入文件

 =IP1
abc[0]
abc[1]
abc[2]
=IP2
def[4]
def[8]
def[9]

我需要得到以下格式的输出 -

=IP1
abc[0-2]
=IP2
def[4,8-9]

我一直在尝试使用哈希来实现上述目的,我在其中读取文件的每一行,然后拆分(使用'[')每一行,我将第一部分作为键并再次读取文件以将值保存在哈希键的数组。但我陷入了一个循环。任何人都可以就如何实现上述目标提供帮助吗?

【问题讨论】:

  • 每个= 行之后的所有值是否都以相同的字符串开头?例如,=IP1 之后的所有行总是以abc 开头还是可以混合使用?括号中的数字是否总是按升序排列?
  • 不,= 之后的值,以不同的名称开头。有一种混合物。其次是的,括号内的数字总是按升序排列。
  • 好的,那么相同的值是否全部组合在一起——例如,所有abcs,然后是所有xyzs,然后是=IP2。括号中的数字是否总是按顺序排列的?
  • 不同的组可以有相同的标签吗?例如,=IP3=IP4 是否都具有 xyz 值?
  • 是的,所有的 abc 和所有的 xyz 都组合在一起了。是的,括号中的数字始终按排序顺序排列。不同的组没有相同的标签。

标签: perl hash


【解决方案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 num2range{ return Set::IntSpan->new(@_) }

Set::IntSpan 对象已重载字符串化,因此打印它会给出范围的文本表示。如果你走这条路,你可以消除对数字列表进行排序的代码——由 Set::IntSpan 内部处理。

【讨论】:

  • 非常感谢!好吧,我对 PERL 还是很陌生,所以能有更简单的代码行来实现同样的目标吗?
  • @RaviRaj:它是 Perl !一般来说,发明您认为行为方式与真实数据相同的数据是错误的;你通常最终会遇到很多人问很多问题(我不得不这样做),或者 OP 不得不用 “谢谢,但事实并非如此” 拒绝完美的答案
【解决方案2】:

好的,这是我对解决方案的看法。如果没有关于传入数据的任何更好的信息,它可能会比必要的更复杂

它保存数据——=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]

【讨论】:

    猜你喜欢
    • 2012-10-07
    • 2013-11-17
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多