Как я могу разобрать этот синтаксис для массива в Perl? - PullRequest
3 голосов
/ 21 сентября 2010

У меня есть файл, который содержит параметры, использующие этот синтаксис

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? >
    }
}
  • Мне нужна помощь в разборе чисел. Для разбора я могу думать только о том, чтобы использовать последовательные разбиения (на -, , и ).Есть ли какой-нибудь лучший (чистый и элегантный способ использования регулярных выражений?)

  • Также приветствуются комментарии / предложения по общей структуре программы.

Ответы [ 7 ]

5 голосов
/ 21 сентября 2010

Взгляните на Text::NumericList модуль от CPAN.Он может конвертировать строки в массив так, как вам нужно:

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)

Вы можете хотя бы взглянуть на его исходный код для идей.

4 голосов
/ 21 сентября 2010

Я бы предложил разбор строки в отдельную переменную, так как $_ имеет тенденцию к засорению другими вызовами функций. Вы можете удалить завершающий символ новой строки одновременно с помощью chomp.

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

Далее вам необходимо определить индикатор «RANGE» и извлечь следующие цифры. Если такого индикатора нет, вы можете просто перейти к следующей строке:

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);
}
3 голосов
/ 21 сентября 2010

Как насчет этого?

Сначала разбейте строку на элементы, разделенные значениями, а затем проверьте, есть ли знак «-» для создания диапазонов:

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;
        }
    } 
}
2 голосов
/ 22 сентября 2010

Мне нравится использовать диапазон Perl и операторы || для такой задачи:

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

Если токен содержит -, оператор split/-/ установит значения $x и $y и добавит диапазон от $x до $y к выводу map. В противном случае он просто установит $x и просто добавит $x к выводу.

1 голос
/ 22 сентября 2010

В тех же строках, что и другие ответы:

#!/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
1 голос
/ 22 сентября 2010

Фильтр дубликатов с хешем:

#! /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
0 голосов
/ 22 сентября 2010

Вот мои усилия:

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 : ();

}

Возвращает пустой список, если строковый параметр не соответствует размеченному вами базовому формату.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...