实际上,我试图解析一些标准文本,这是shell命令的输出。
pool: thisPool
state: ONLINE
status: Some supported features are not enabled on the pool. The pool can
still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
the pool may no longer be accessible by software that does not support
the features. See zpool-features(5) for details.
scan: none requested
config:
NAME STATE READ WRITE CKSUM
homePool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F ONLINE 0 0 0
cache
ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE 0 0 0
errors: No known data errors
我希望使用Perl6语法,并希望在单独的令牌或正则表达式中捕获每个字段。因此,我做了以下语法:
grammar zpool {
regex TOP { \s+ [ <keyword> <collection> ]+ }
token keyword { "pool: " | "state: " | "status: " | "action: " | "scan: " | "config: " | "errors: " }
regex collection { [<:!keyword>]* }
}
我的想法是regex找到一个关键字,然后开始收集所有的数据直到下一个关键字。然而,每次我只得到“池:”->所有剩余的文本。
keyword => 「pool: 」
collection => 「homePool
state: ONLINE
status: Some supported features are not enabled on the pool. The pool can
still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
the pool may no longer be accessible by software that does not support
the features. See zpool-features(5) for details.
scan: none requested
config:
NAME STATE READ WRITE CKSUM
homePool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F ONLINE 0 0 0
cache
ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE 0 0 0
errors: No known data errors
」
我不知道如何让它停止吃字符时,它找到一个关键字,然后把它作为另一个关键字。
发布于 2017-12-02 06:45:33
问题1
您已经编写了<:!keyword>
而不是<!keyword>
。这不是你想要的。您需要删除:
。
<:foo>
语法在P6 regex 将单个字符与指定的Unicode属性匹配。中,在本例中是属性:foo
,这反过来意味着:foo(True)
。
<:!keyword>
将单个字符与Unicode属性:keyword(False)
匹配。
但是没有Unicode属性:keyword
。
因此,否定断言总是正确的,每次都会匹配一个输入字符。
因此,正如你所知道的那样,模式只是在文本的其余部分中咀嚼着自己的方式。
问题2
一旦修复了问题1,就会出现第二个问题。
<:!keyword>
将单个字符与Unicode属性:keyword(False)
匹配。每次匹配时,它都会自动咀嚼一些输入(一个字符)。
相反,如果<!keyword>
匹配,则不会使用任何输入。您必须确保使用它的模式会咀嚼输入。
在解决了这两个问题之后,您将得到预期的输出。(您将看到的下一个问题是,config
关键字不起作用,因为输入文件示例中的:
在config:
中没有空格。)
所以,通过一些清理:
my @keywords = <pool state status action scan config errors> ;
say grammar zpool {
token TOP { \s+ [ <keyword> <collection> ]* }
token keyword { @keywords ': ' }
token collection { [ <!keyword> . ]* }
}
我已经将所有模式转换为token
声明。通常,除非您知道需要其他的东西,否则始终使用token
。(regex
支持回溯。如果你不小心的话,这会大大减缓事情的发展。rule
使规则中的空格非常重要。)
我已经将关键字提取到数组中了。@keywords
的意思是@keywords[0] | @keywords[1] | ...
。
我在最后一个模式中在<!keyword>
之后添加了一个<!keyword>
(为了使用一个字符的输入值,以避免在<!foo>
不消耗任何输入的情况下会发生的无限循环)。
如果您还没有看到它们,请注意,可用语法调试选项是您的朋友。
Hth
发布于 2017-12-02 21:16:46
虽然我有时喜欢一个好的语法,但是通过调用拆分来解决这个问题要容易得多。
my $input = q:to/EOF/;
pool: thisPool
state: ONLINE
status: Some supported features are not enabled on the pool. The pool can
still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
the pool may no longer be accessible by software that does not support
the features. See zpool-features(5) for details.
scan: none requested
config:
NAME STATE READ WRITE CKSUM
homePool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C ONLINE 0 0 0
ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F ONLINE 0 0 0
cache
ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE 0 0 0
errors: No known data errors
EOF
my @delimiter = <pool state status action scan config errors>;
my %fields;
for $input.split( / ^^ \h* (@delimiter) ':' \h*/, :v)[1..*] -> $key, $value {
%fields{ $key[0] } = $value.trim;
}
say %fields.perl;
这是通过对已知的键进行拆分,丢弃第一个元素(因为我们知道输入以键而不是值开始),然后在锁步中迭代键和值。
现在,既然您要求语法,我们可以很容易地将split
调用转换为纯正则表达式,方法是将每个值替换为.+?
(任何字符串,但尽可能短)。
现在,让我们给它更多的结构:
my @delimiter = <pool state status action scan config errors>;
grammar ZPool {
regex key { @delimiter }
regex keychunk { ^^ \h* <key> ':' }
regex value { .*? }
regex chunks { <keychunk> \h* <value> }
regex TOP { <chunks>+ }
}
我们可以从嵌套的匹配树中提取结果,或者使用有状态的action对象进行欺骗:
class ZPool::Actions {
has $!last-key;
has %.contents;
method key($m) { $!last-key = $m.Str }
method value($m) { %!contents{ $!last-key } = $m.trim }
}
然后用它:
my $actions = ZPool::Actions.new;
ZPool.parse($input, :$actions);
say $actions.contents.perl;
key
和keychunk
不需要回溯,所以您可以将它们从regex
更改为token
。
当然,使用.+?
和回溯可以被认为是欺骗,所以您可以使用raiph提到的技巧,在value
正则表达式中进行负面展望。
https://stackoverflow.com/questions/47602397
复制相似问题