抛弃它的主要可能是\s
匹配水平和垂直空间。要仅匹配水平空间,请使用\h
,而仅要匹配垂直空间\v
。
我的一个小建议是避免在令牌中包含换行符。您可能还想使用交替运算符%
或%%
,因为它们是为处理这种类型的工作而设计的:
grammar Parser {
token TOP {
<headerRow> \n
<valueRow>+ %% \n
}
token headerRow { <.ws>* %% <header> }
token valueRow { <.ws>* %% <value> }
token header { \S+ }
token value { \S+ }
token ws { \h* }
}
其结果Parser.parse($dat)
如下:
「ID Name Email
1 test test@email.com
321 stan stan@nowhere.net
」
headerRow => 「ID Name Email」
header => 「ID」
header => 「Name」
header => 「Email」
valueRow => 「 1 test test@email.com」
value => 「1」
value => 「test」
value => 「test@email.com」
valueRow => 「 321 stan stan@nowhere.net」
value => 「321」
value => 「stan」
value => 「stan@nowhere.net」
valueRow => 「」
这说明语法已经成功解析了所有内容。但是,让我们集中讨论问题的第二部分,即您希望它在变量中可用。为此,您需要提供一个对于该项目非常简单的动作类。您只需要创建一个其方法与您的语法方法相匹配的类即可(尽管非常简单的方法(例如value
/ header
,除了字符串化不需要其他特殊处理,可以忽略)。还有一些更具创意/紧凑的方式来处理您的信息,但我将以一种非常基本的方式进行说明。这是我们的课:
class ParserActions {
method headerRow ($/) { ... }
method valueRow ($/) { ... }
method TOP ($/) { ... }
}
每个方法都有签名($/)
,它是正则表达式匹配变量。现在,让我们问一下每个令牌需要什么信息。在标题行中,我们希望每个标题值都在一行中。所以:
method headerRow ($/) {
my @headers = $<header>.map: *.Str
make @headers;
}
上有一个量词任何令牌将被视为一个Positional
,所以我们也可以访问每一个人头匹配$<header>[0]
,$<header>[1]
等等。但是这些都是比赛的对象,所以我们只是快速字符串化他们。该make
命令允许其他令牌访问我们创建的特殊数据。
我们的值行看起来将相同,因为$<value>
令牌是我们所关心的。
method valueRow ($/) {
my @values = $<value>.map: *.Str
make @values;
}
当我们到达最后一个方法时,我们将要创建带有哈希的数组。
method TOP ($/) {
my @entries;
my @headers = $<headerRow>.made;
my @rows = $<valueRow>.map: *.made;
for @rows -> @values {
my %entry = flat @headers Z @values;
@entries.push: %entry;
}
make @entries;
}
在这里你可以看到我们是如何获得我们在处理的东西headerRow()
和valueRow()
:您使用.made
方法。因为有多个valueRows,所以要获取它们的每个made
值,我们需要做一个映射(在这种情况下,我倾向于将我的语法写成仅包含<header><data>
在语法中,并将数据定义为多行,但这是很简单,还算不错)。
现在,我们将标头和行包含在两个数组中,只需要使它们成为哈希数组即可,我们在for
循环中执行此操作。在flat @x Z @y
刚刚intercolates的元素,哈希分配做什么?我们的意思,但也有其他的方式来得到你想要的哈希数组。
一旦完成,就make
可以了,然后它将made
在解析的中可用:
say Parser.parse($dat, :actions(ParserActions)).made
-> [{Email => test@email.com, ID => 1, Name => test} {Email => stan@nowhere.net, ID => 321, Name => stan} {}]
将它们包装到一个方法中是很常见的,例如
sub parse-tsv($tsv) {
return Parser.parse($tsv, :actions(ParserActions)).made
}
这样你就可以说
my @entries = parse-tsv($dat);
say @entries[0]<Name>; # test
say @entries[1]<Email>; # stan@nowhere.net
Nil
。就反馈而言,这是相当荒谬的,对吧?要进行调试,请下载commaide(如果尚未安装),和/或请参阅如何改进语法中的错误报告?。您会发现Nil
您的模式采用了回溯语义。看到我对此的回答。我建议您避免回溯。请参阅@ user0721090601的答案。有关纯粹的实用性和速度,请参阅JJ的答案。另外,“我想用Raku解析X。有人可以帮忙吗?”的介绍性一般性回答。。