如何使用 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
参数,因此您目前无法设置段落模式。
此外,有些习语被一些人认为是不好的做法,例如:
后缀 if 语句,例如say 'hello' if $y == 0
.
依赖代码中的隐式 $_
变量,例如.say
因此,根据您的立场,这在 Perl 中会被认为是不好的做法。
我想使用惯用的 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
参数,因此您目前无法设置段落模式。
此外,有些习语被一些人认为是不好的做法,例如:
后缀 if 语句,例如
say 'hello' if $y == 0
.依赖代码中的隐式
$_
变量,例如.say
因此,根据您的立场,这在 Perl 中会被认为是不好的做法。