【问题标题】:Parse Perl range string such as "1,4..7,10,11" [duplicate]解析 Perl 范围字符串,例如“1,4..7,10,11”[重复]
【发布时间】:2015-05-26 15:59:27
【问题描述】:

给定一个字符串,例如1,4..7,10,11,有没有一种巧妙的方法可以将它解析到列表(1,4,5,6,7,10,11) 中?该字符串作为命令行参数给出。我的计划是确保字符串适合/^[0-9.,]+$/,然后适合eval,但是如果没有eval,有没有更简洁的方法?

【问题讨论】:

  • 显示您尝试过的内容。
  • @Jens 我不明白,我只是在问题中写了这个。我实现它没有问题,只是想知道是否有一个更整洁的解决方案而不使用eval

标签: perl parsing eval


【解决方案1】:

看看 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)

$list->set_string('1..3,5...7');
@array = $list->get_array;        # Returns (1,2,3,5,6,7)

正如@dgw 在评论中提到的其他可能的模块是:

Number::Range
Set::Intspan

【讨论】:

【解决方案2】:

当然,最简单和丑陋的解决方案是像这样使用spliteval

$str = "1,4..7,10,11";

my @out;
for (split ",", $str) {
    if (/\d+\.\.\d+/) {
        eval('push @out, $_ for '.$_);
    }
    else {
        push @out, $_;
    }
}
print "(", join(',', sort {$a <=> $b} @out), ")"; 

您可以通过使用/([0-9]+)[.][.]([0-9]+)/ 也可写/(\d+)\.{2}(\d+)/ 提取范围的两个成员来避免eval

$str = "1,4..7,10,11";

my @out;
for (split ",", $str) {
    if (/(\d+)\.\.(\d+)/) {
        push @out, $_ for $1 .. $2;
    }
    else {
        push @out, $_;
    }
}
print "(", join(',', sort {$a <=> $b} @out), ")"; 

由此我们可以简化代码:

$str = "1,4..7,10,11";

my @out;
for (split ",", $str) {
    push @out, $_ for (/(\d+)\.\.(\d+)/)? $1 .. $2: $_; 
}
print "(", join(',', sort {$a <=> $b} @out), ")"; 

最后但同样重要的是,如果您使用bash 并且您的输入字符串作为参数传递给您的程序。您可以像这样使用大括号扩展:

$ echo 1 2 {3..6} 10 11 | sort
1 2 3 4 5 6 10 11

【讨论】:

  • 当 CPAN 上有许多经过更好测试和记录的模块时,不要重新发明轮子。只是我的 2 美分。
  • 我非常同意你的论点。但有时无法安装 CPAN 模块。在我的公司,我们无法访问 CPAN,使用 Perl 脚本的人不知道如何手动安装所有缺少的模块及其所有依赖项。此外,这类问题非常简单,按照 KISS 规则,我们并不总是需要复杂的模块(需要安装)来解决非常简单和具体的任务。
  • 如果你还是要评估,为什么不评估整个字符串呢? @out = eval "(1,4..7,10,11)";
猜你喜欢
  • 1970-01-01
  • 2014-07-19
  • 2021-06-17
  • 2016-12-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 2015-03-11
  • 2011-07-03
相关资源
最近更新 更多