我有一个包含使用此语法的参数的文件
RANGE {<value> | <value>-<value>} [ , ...]
其中value
是数字。
所有这些都是有效的语法
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? >
}
}
我需要帮助解析数字。对于解析,我能想到的一种方法是使用连续的分割(-
,,
和{{ 1}})。是否有更好的(干净优雅,使用正则表达式?)方式?
此外,欢迎就整体计划结构提出意见/建议。
答案 0 :(得分:5)
查看CPAN的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)
您至少可以查看其源代码以获取创意。
答案 1 :(得分:4)
我建议将该行解析为一个单独的变量,因为$_
往往会受到其他函数调用的破坏。您可以使用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);
}
答案 2 :(得分:3)
这个怎么样?
首先将该行拆分为由值分隔的元素,然后检查是否有“ - ”符号来创建范围:
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;
}
}
}
答案 3 :(得分:2)
我喜欢使用Perl的范围和||
运算符来解决这个问题:
map { my($x,$y)=split/-/; $x..$y||$x } split /\s*,\s*/;
如果令牌包含-
,split/-/
语句将同时设置$x
和$y
,并将范围从$x
添加到$y
到map
输出。否则,它只会设置$x
,只需将$x
添加到输出中。
答案 4 :(得分:1)
使用哈希过滤重复项:
#! /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
答案 5 :(得分:1)
与其他答案一样:
#!/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
答案 6 :(得分:0)
这是我的努力:
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 : ();
}
如果字符串参数不符合您布置的基本格式,则返回空列表。