代码之家  ›  专栏  ›  技术社区  ›  Aakash Goel

如何在Perl中将此语法解析为数组?

  •  3
  • Aakash Goel  · 技术社区  · 15 年前

    RANGE {<value> | <value>-<value>} [ , ...]
    

    value s是数字。

    RANGE 34
    RANGE 45, 234
    RANGE 2-99
    RANGE 3-7, 15, 16, 2, 54
    

    如何在Perl中将值解析为数组?

    例如,在最后一个示例中,我希望我的数组 3, 4, 5, 6, 7, 15, 16, 2, 54 . 元素的顺序无关紧要。


    - 要确定是否存在范围,请使用循环解析该范围,然后解析其余元素

    my @arr;
    my $fh, "<", "file.txt" or die (...);
    while (<$fh>) {
        if ($_ =~ /RANGE/) {
            if ($_ =~ /-/) { # parse the range
                < how do I parse the lower and upper limits? >
                for($lower..$upper) {
                    $arr[++$#arr] = $_;
                }
            } else { # parse the first value
                < how do I parse the first value? >
            }
    
            # parse the rest of the values after the comma
            < how do I parse the values after the comma? >
        }
    }
    
    • 对于解析,我可以想到的一种方法是使用连续拆分(on) - , , ). 有没有更好的(干净优雅,也许使用regex?)怎么了?

    • 此外,欢迎对整个课程结构提出意见/建议。

    7 回复  |  直到 15 年前
        1
  •  4
  •   Ether    15 年前

    我建议将行解析为一个单独的变量,如下所示 $_ chomp .

    while (<$fh)>
    {
        chomp (my $line = $_);
        # ...
    }
    

    next if $line !~ /^RANGE (.*)$/;
    

    现在,您可以开始提取数字,在逗号分隔符上拆分:

    my @ranges = split /, /, $1;
    

    .. 运算符;否则,请别管号码:

    @ranges = map { /(\d+)-(\d+)/ ? ($1 .. $2) : $_ } @ranges;
    

    把所有这些放在一起,并结合表达式,我们得到:

    my @numbers;
    while (<$fh)>
    {
        chomp (my $line = $_);
        next if $line !~ /^RANGE (.*)$/;
    
        push @numbers, map { /(\d+)-(\d+)/ ? ($1 .. $2) : $_ } (split /, /, $1);
    }
    
        2
  •  5
  •   Ivan Nevostruev    15 年前

    看一看 Text::NumericList

    use Text::NumericList;
    my $list = Text::NumericList->new;
    
    $list->set_string('1-3,5-7');
    my @array = $list->get_array;     # Returns (1,2,3,5,6,7)
    

    你至少可以看看它的源代码来寻找灵感。

        3
  •  3
  •   eumiro    15 年前

    这个怎么样?

    首先将行拆分为由值分隔的元素,然后检查是否有“-”符号来创建范围:

    if ($line =~ /RANGE ([\d\,\- ]+)/) {
        my $paramtxt = $1;
        my @elements = split(/\,/, $paramtxt);
        for my $element (@elements) {
            if ($element =~ /(\d+)\-(\d+)/) {
                $lower = $1;
                $upper = $2;
                push @arr, $lower .. $upper;
            } elsif ($element =~ /(\d+)/) {
                $solo = $1;
                push @arr, $solo;
            }
        } 
    }
    
        4
  •  2
  •   mob    15 年前

    我喜欢使用Perl的范围和 ||

    map {  my($x,$y)=split/-/; $x..$y||$x } split /\s*,\s*/;
    

    如果令牌包含 - ,的 split/-/ 语句将同时设置 $x $y 并将范围从 x美元 map 输出。否则,它将只是设置 x美元 x美元 到输出。

        5
  •  1
  •   Greg Bacon    15 年前

    #! /usr/bin/perl
    
    use warnings;
    use strict;
    
    use 5.10.0;
    
    my @tests = (
      "RANGE 34",
      "RANGE 45, 234",
      "RANGE 2-99",
      "RANGE 3-7, 15, 16, 2, 54",
    );
    
    for (@tests) {
      my %hits;
      @hits{$1 .. $2 // $1} = ()
        while /(\d+)(?:-(\d+))?/g;
    
      my @array = sort { $a <=> $b } keys %hits;
      print "@array\n";
    }
    

    输出:

    34
    45 234
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    2 3 4 5 6 7 15 16 54
        6
  •  1
  •   Sinan Ünür    15 年前

    与其他答案一样:

    #!/usr/bin/perl
    
    use strict; use warnings;
    
    my $number = '[0-9]+';
    my $range  = "$number(:?-$number)?";
    my $ranges = "$range(:?, $range)*";
    my $pattern = qr/^RANGE ($ranges)$/;
    
    
    while ( my $range = <DATA> ) {
        next unless $range =~ $pattern;
        my $expanded = expand_ranges($1);
        print "@$expanded\n\n";
    }
    
    sub expand_ranges {
        my ($ranges) = @_;
        my @terms = split /, /, $ranges;
        my @expanded;
    
        for my $term ( @terms ) {
            my ($lo, $hi) = split /-/, $term;
            push @expanded, defined( $hi ) ? $lo .. $hi : $lo .. $lo;
        }
    
        return \@expanded;
    }
    
    
    __DATA__
    RANGE 34
    RANGE 45, 234
    RANGE 2-99
    RANGE 3-7, 15, 16, 2, 54
    

    34
    
    45 234
    
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3
    1 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
     85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    
    3 4 5 6 7 15 16 2 54
        7
  •  0
  •   Sean    15 年前

    以下是我的努力:

    sub parse_range {
        my $str = shift;
        return unless $str =~ /^RANGE /g;
    
        my @array;
        while ($str =~ / \G \s* ( \d+ ) ( - ( \d+ ) ) ? \s* (?: , | $ ) /gxc) {
            push @array, $2 ? $1 .. $3 : $1;
        }
    
        return $str =~ /\G$/ ? @array : ();
    
    }
    

    如果字符串参数不符合您设置的基本格式,它将返回一个空列表。