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

问题描述 投票:2回答:2

我想使用惯用的Perl 6进行此操作。

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

我想简单地打印出以Cluster Unique开头的标题行及其后的所有行,直到但不包括第一次出现的空行。这是文件的样子:

</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
parsing text perl6 raku flip-flop
2个回答
3
投票

我想使用惯用的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. Postif if语句,例如say 'hello' if $y == 0

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

因此,根据您所居住的篱笆的哪一侧,在Perl中将其视为bad做法。


6
投票

单线版本

.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+匹配一个或多个空格
  • ffflip-flop operator。只要左边的表达式为假,它就是假的。当其左侧的表达式被评估为true时,它将变为true。当右边的表达式变为真时,它将变为假,并且永远不会再次被评估为真。在这种情况下,如果我们使用^ff^而不是ff^,则标头将不包含在输出中。
  • [当^ff之前(或之后)时,它会修改ff,因此,其左侧(或右侧)表达式变为true的迭代也为假。
  • /^\*$/匹配一个空行
    • [^匹配字符串的开头
    • [\s*匹配零个或多个空格
    • [$匹配字符串的结尾

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

© www.soinside.com 2019 - 2024. All rights reserved.