如何使用 Perl 6 从嘈杂的文件中提取一些数据?

How can I extract some data out of the middle of a noisy file using Perl 6?

我想使用惯用的 Perl 6 来完成此操作。

我在嘈杂的输出文件中发现了一个很棒的连续数据块。

我想简单地打印出以 Cluster Unique 开头的 header 行及其后的所有行,直到但不包括第一次出现的空行。文件如下所示:

</path/to/projects/projectname/ParameterSweep/1000.1.7.dir> was used as the working directory.
....

Cluster Unique Sequences    Reads   RPM
1   31  3539    3539
2   25  2797    2797
3   17  1679    1679
4   21  1636    1636
5   14  1568    1568
6   13  1548    1548
7   7   1439    1439

Input file: "../../filename.count.fa"
...

这是我要解析的内容:

Cluster Unique Sequences    Reads   RPM
1   31  3539    3539
2   25  2797    2797
3   17  1679    1679
4   21  1636    1636
5   14  1568    1568
6   13  1548    1548
7   7   1439    1439

One-liner版本

.say if /Cluster \s+ Unique/ ff^ /^\s*$/ for lines;

英语

打印输入文件中的每一行,从包含短语 Cluster Unique 的那一行开始,到下一个空行之前结束。

带有注释的相同代码

.say                    # print the default variable $_
if                      # do the previous action (.say) "if" the following term is true
/Cluster \s+ Unique/    # Match $_ if it contains "Cluster Unique"
ff^                     # Flip-flop operator: true until preceding term becomes true
                        #                     false once the term after it becomes true
/^\s*$/                 # Match $_ if it contains an empty line
for                     # Create a loop placing each element of the following list into $_
lines                   # Create a list of all of the lines in the file
;                       # End of statement

扩展版

for lines() {
    .say if (
        $_ ~~ /Cluster \s+ Unique/  ff^  $_ ~~ /^\s*$/
    )
}
  • lines() 类似于 perl5 中的 <>。一次读取命令行中列出的每个文件的每一行。由于这是在 for 循环中,因此每一行都放在默认变量 $_.
  • say 类似于 print,只是它还附加了一个换行符。当以 . 开头编写时,它直接作用于默认变量 $_.
  • $_ 是默认变量,在本例中包含文件中的一行。
  • ~~ 是将 $_ 与正则表达式进行比较的匹配运算符。
  • // 在两个正斜杠之间创建一个正则表达式
  • \s+匹配一个或多个空格
  • ff就是flip-flop operator。只要它左边的表达式是假的,它就是假的。当其左侧的表达式被评估为真时,它变为真。当其右侧的表达式变为真并且再也不会被评估为真时,它变为假。在这种情况下,如果我们使用 ^ff^ 而不是 ff^,那么 header 将不会包含在输出中。
  • ^出现在ff之前(或之后)时,它修改ff,使其左边(或右边)的表达式变为真的迭代也是假的.
  • /^\*$/匹配一个空行
    • ^ 匹配字符串的开头
    • \s* 匹配零个或多个空格
    • $ 匹配字符串的结尾

顺便说一句,Perl 5 中的 flip-flop 运算符在标量上下文中是 ..(它是列表上下文中的范围运算符)。但它的功能当然不如 Perl 6 丰富。

I would like to do this using idiomatic Perl 6.

Perl 中,在文件中定位块的惯用方法是以 段落模式 读取文件,然后停止读取当你找到你感兴趣的块时文件。如果你正在读取一个 10GB 的文件,并且该块位于文件的顶部,那么继续读取文件的其余部分是低效的——更不用说执行 if 测试了在文件的每一行。

在 Perl 6 中,您可以像这样一次阅读一个段落:

my $fname = 'data.txt';

my $infile = open(
    $fname, 
    nl => "\n\n",   #Set what perl considers the end of a line.
);  #Removed die() per Brad Gilbert's comment. 

for $infile.lines() -> $para {  
    if $para ~~ /^ 'Cluster Unique'/ {
        say $para.chomp;
        last;   #Quit reading the file.
    }
}

$infile.close;

#    ^                   Match start of string.
#   'Cluster Unique'     By default, whitespace is insignificant in a perl6 regex. Quotes are one way to make whitespace significant.   

但是,在 perl6 rakudo/moarVM 中,open() 函数无法正确读取 nl 参数,因此您目前无法设置段落模式。

此外,有些习语被一些人认为是不好的做法,例如:

  1. 后缀 if 语句,例如say 'hello' if $y == 0.

  2. 依赖代码中的隐式 $_ 变量,例如.say

因此,根据您的立场,这在 Perl 中会被认为是不好的做法。