首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Perl脚本|使用HASHes

Perl脚本|使用HASHes
EN

Stack Overflow用户
提问于 2015-08-03 11:48:48
回答 2查看 93关注 0票数 0

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

代码语言:javascript
复制
 =IP1
abc[0]
abc[1]
abc[2]
=IP2
def[4]
def[8]
def[9]

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

代码语言:javascript
复制
=IP1
abc[0-2]
=IP2
def[4,8-9]

我一直在尝试使用散列来实现上述目的,我读取文件的每一行,然后拆分(用‘[’)每一行,我保留第一部分作为键,并再次读取文件以将值保存在散列键的数组中。但是我陷入了一个循环中。有谁能提供如何实现上述目标的帮助吗?

EN

回答 2

Stack Overflow用户

发布于 2015-08-03 13:03:44

有几个有趣的子问题。首先,您希望跟踪最新的头文件(即=IP1)。其次,您希望跟踪与某些键关联的数字列表,第三,您希望生成范围字符串。

下面是我会怎么做:

代码语言:javascript
复制
#!/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]

将生成以下输出:

代码语言:javascript
复制
=IP1
abc[0-2]
=IP2
def[4,8-9]

如果Borodin对你的原始帖子的评论中的一些问题的答案得到了回答,这个解决方案还可以进一步优化。例如,如果我们知道数字已经按顺序排列,那么就没有必要在生成范围之前对数字列表进行排序。如果我们更多地了解"abc“和"def”是什么,一些复杂性(和计算工作)可能会被消除。如果排序顺序无关紧要,我们可以在减少工作量的同时进一步简化。

此外,Set::IntSpan模块可能会提供一种更健壮的方法来生成范围字符串,如果此脚本的使用期限超过“一次性”,则可能值得考虑。如果您选择使用Set::IntSpan,则您的num2range子对象可能如下所示:

代码语言:javascript
复制
sub num2range{ return Set::IntSpan->new(@_) }

Set::IntSpan对象重载了字符串化,因此打印它会给出范围的文本表示形式。如果采用这种方式,就可以省去对数字列表进行排序的代码--这是由Set::IntSpan内部处理的。

票数 1
EN

Stack Overflow用户

发布于 2015-08-03 16:57:22

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

它保持数据--包括=IP头和xyz[9]值--的顺序与它们第一次遇到时的顺序相同。我已经将数字范围收缩的生成分离到子例程ranges

只需将文件中的数据读取到数据结构%data@order中,然后再将它们打印出来。散列的@order数组和_order子键用于保存遇到值并在将新键插入相应散列时添加到的序列

代码语言:javascript
复制
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;
}

输出

代码语言:javascript
复制
=IP1
abc[0-2]
=IP2
def[4,8-9]
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31779141

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档