如何将间隔列表转换为包含在这些间隔内的数字列表?

How to convert a list of intervals into the list of numbers comprised within these intervals?

我有一个大文件,如下所示:

esup_255_3      transdecoder   7655    8192         
esup_6093_1     transdecoder   2732    2774        
esup_25727_1    transdecoder   1       60 
...  

第 3 列和第 4 列代表数字区间。

我正在尝试修改此文件以包含间隔内的数字列表,列在不同的列中(此处为第 5 列),如下所示:

esup_255_3      transdecoder    7655    8192    7655     
esup_255_3      transdecoder    7655    8192    7656
esup_255_3      transdecoder    7655    8192    7657 
esup_255_3      transdecoder    7655    8192     ...    
esup_255_3      transdecoder    7655    8192    8192    
esup_6093_1     transdecoder    2732    2774    2732     
esup_6093_1     transdecoder    2732    2774    2733     
esup_6093_1     transdecoder    2732    2774    ....     
esup_6093_1     transdecoder    2732    2774    2774     
... and so on...

我认为 Perl 可能对此有所帮助,但我对它还很陌生。我只精通bash,在这里我似乎无法找到我需要的正确方法

是这样的吗?

    perl -lne 'my ($line, $from, $to) = /^(.*\s(\d+)\s+(\d+)\s*)$/; print "$line\t$_" for $from..$to;' 

当我 运行 它在你的代码片段上时,它打印出 641 行:

esup_255_3      transdecoder   7655    8192             7655
esup_255_3      transdecoder   7655    8192             7656
esup_255_3      transdecoder   7655    8192             7657
[...]
esup_255_3      transdecoder   7655    8192             8190
esup_255_3      transdecoder   7655    8192             8191
esup_255_3      transdecoder   7655    8192             8192
esup_6093_1     transdecoder   2732    2774         2732
esup_6093_1     transdecoder   2732    2774         2733
[...]
esup_6093_1     transdecoder   2732    2774         2773
esup_6093_1     transdecoder   2732    2774         2774
esup_25727_1    transdecoder   1       60   1
esup_25727_1    transdecoder   1       60   2
[...]
esup_25727_1    transdecoder   1       60   59
esup_25727_1    transdecoder   1       60   60

解释如下。让我们从选项开始:

perl -lne

我们将从右到左带他们。 -e(表示“执行”或“求值”)只是告诉 Perl 命令行上的下一件事是 运行 的代码,因此它不会在标准输入上寻找代码。

-n 告诉它自动迭代其输入 line-by-line;它的作用就好像在实际代码周围有一个 while (<>) {...} 循环。在循环体内,当前行将在主题变量 $_.

中找到

-l 告诉它从输入中去除换行符并自动将一个换行符附加到打印出的每个字符串;这基本上消除了图片中的换行符并简化了逻辑。

因此程序将读取输入 line-by-line 和 运行 作为参数给定的代码 -e 每行。让我们看一下这段代码,它以这条语句开头:

my ($line, $from, $to) = /^(.*\s(\d+)\s+(\d+)\s*)$/;

正则表达式没有要匹配的显式字符串,因此它会自动匹配具有当前行的 $_。它必须匹配整行(因为 ^ 在开头, $ 在结尾)。由于最外面的括号,实际的行值也被捕获,因此它将是匹配返回的第一项,分配给变量 $line.

该行的第一部分可以是任何内容(因为 .* 匹配所有内容),所以我们真正关注的是字符串的结束方式而不是它的开始方式。第一个感兴趣的项目是任何白色 space 字符 (\s),这是为了确保我们不会错过以下任何数字。具体来说,我们正在寻找括号捕获的一个或多个数字 (\d+),以便匹配也将返回该值;这是第二次捕获,所以它进入赋值中的第二个变量,$from。在这些数字之后,我们寻找更多的 whitespace (至少需要一个 whitespace 字符,但允许任何数字),然后是另一序列数字;第二组数字再次被捕获并返回,所以它在最后一个变量 $to 中结束。最后,我们允许最后一组数字后跟任意数量的可选尾随 space.

因此,在阅读第一行 $_ = "esup_255_3 transdecoder 7655 8192 " 后,匹配 + 赋值会将 $line 设置为整个字符串的副本,$from 将设置为 7655,并且$to8192.

然后我们来输出。这一行:

print "$line\t$_" for $from .. $to;

是一个更短的循环写法:

foreach $_  ($from .. $to) {
   print "$line\t$_";
}

这意味着它循环遍历从 $from$to 的整个数字,重新使用 $_ 作为循环控制变量(这就是为什么我们必须将当前行复制到 $line).对于范围内的每个值,它打印出整行的副本,后跟一个制表符和当前数字。