我有一个像"39 3A 3B 9:;"
这样的字符串,我想提取“ 39、3A,3B”
我尝试过
my $a = "39 3A 3B 9:;";
grammar Hex {
token TOP { <hex_array>+ .* }
token hex_array { <[0..9 A..F]> " " }
};
Hex.parse($a);
但这似乎不起作用。 甚至这似乎也不起作用。
my $a = "39 3A 3B ";
grammar Hex {
token TOP { <hex_array>+ }
token hex_array { <[0..9 A..F]> " " }
};
Hex.parse($a);
我确实尝试过语法:: Tracer TOP和hex_array都失败了
TOP
| hex_array
| * FAIL
* FAIL
答案 0 :(得分:14)
<[abcdef...]>
是匹配-一个-字符意义上的“字符类”。 1
获取所需内容的惯用方式是使用the **
quantifier:
my $a = "39 3A 3B ";
grammar Hex {
token TOP { <hex_array>+ }
token hex_array { <[0..9 A..F]>**1..2 " " }
};
Hex.parse($a);
此答案的其余部分是关于“为什么”以及如何使用rule
的“附加”材料。
您当然可以完全自由地匹配空白情况,只需将空白模式包含在任意单个令牌中,就像您在" "
令牌中使用hex_array
一样。
但是,在大多数情况下,最好在适当的时候使用rule
s代替。
ws
代替“”,\s*
等。让我们删除第二个token
中的空格,然后将其移至第一个:
token TOP { [ <hex_array> " " ]+ }
token hex_array { <[0..9 A..F]>**1..2 }
我们添加了将[...]
和一个空格组合在一起的方括号(hex_array
),然后将+
量词应用于该组合的原子。这是一个简单的更改,语法继续像以前一样工作,与以前一样匹配空格,只是现在hex_array
标记不会捕获空格。
接下来,让我们切换到使用内置的ws
token
:
token TOP { [ <hex_array> <.ws> ]+ }
在理想的情况下,默认的<ws>
比\s*
更有用。 2 如果默认的ws
无法满足您的需求您可以指定自己的ws
令牌。
我们使用<.ws>
代替了<ws>
,因为与\s*
一样,<.ws>
的使用避免了额外的空白捕获,而空白捕获可能只会使解析树变得混乱,并且浪费内存。
在将令牌串在一起的更高级别的解析规则中,几乎每个令牌之后,人们常常想要<.ws>
之类的东西。但是,如果只是像这样明确地编写它,那将是高度重复的并且分散了<.ws>
和[ ... <.ws> ]
的样板。为避免这种情况,有一个内置的隐式快捷方式,它表示为您插入样板的默认假设。此快捷方式是rule
声明符,它依次使用:sigspace
。
rule
(使用:sigspace
) rule
与token
完全相同,除了它在模式开始时打开:sigspace
之外:
rule { <hex_array>+ }
token { :sigspace <hex_array>+ } # exactly the same thing
无 :sigspace
(因此默认情况下在token
和regex
中),模式中的所有文字空间(除非您引用它们)被忽略。通常,对于单个 token
的可读模式,这是理想的,因为它们通常指定要匹配的文字内容。
但是:sigspace
生效后,原子后的空间变得“重要”,因为它们被隐式转换为<.ws>
或[ ... <.ws> ]
调用。这对于指定标记或子规则序列的可读模式是理想的,因为它是避免所有这些额外调用混乱的自然方法。
下面的第一个模式将匹配一个或多个hex_array
令牌,它们之间或末尾没有空格匹配。最后两个将匹配一个或多个hex_array
,且中间没有空格,然后在最后端带有或不带有空格:
token TOP { <hex_array>+ }
# ^ ignored ^ ^ ignored
token TOP { :sigspace <hex_array>+ }
# ^ ignored ^ ^ significant
rule TOP { <hex_array>+ }
# ^ ignored ^ ^ significant
NB 。副词(例如:sigspace
)不是原子。第一个 atom 之前之前的空格(在上面, <hex_array>
之前的空格)从不有效(无论:sigspace
是否有效)。但是此后,如果:sigspace
有效,则模式中所有未引用的空格都是“有效的”,也就是说,它将转换为<.ws>
或[ ... <.ws> ]
。
在上面的代码中,第二个标记和规则将单个 hex_array
与之后的空格匹配,因为+
之后和{{ 1}}表示该模式被重写为:
}
但是如果您输入的多个 token TOP { <hex_array>+ <.ws> }
令牌之间有一个或多个空格 ,则此重写的令牌将不匹配 。相反,您可能要写:
hex_array
它被重写为:
rule TOP { <hex_array> + }
# ignored ^ ^ ^ both these spaces are significant
这将与您的输入匹配。
因此,在经过所有这些看似复杂的操作之后,实际上这只是我的详尽描述,我建议您将原始代码编写为:
token TOP { [ <hex_array> <.ws> ]+ <.ws> }
这将比您的原始版本更灵活(我认为这将是一件好事,尽管对于某些用例当然不会),并且对于大多数P6er来说可能更容易阅读。
最后,要加强如何避免my $a = "39 3A 3B ";
grammar Hex {
rule TOP { <hex_array> + }
token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
的三个杂物中的两个,请参阅What's the best way to be lax on whitespace in a perl6 grammar?。 (第三个问题是,是否需要在原子和量词之间放置一个空格,就像上面的rule
和<hex_array>
之间的空格一样。)
1 如果要匹配多个字符,则将适当的量词附加到字符类。这是将事物变成the assumed behavior of a "character class" according to Wikipedia的明智方式。不幸的是,P6文档当前使该问题感到困惑,例如将真正的字符类和与多个字符匹配的其他规则(在标题Predefined character classes下合并在一起)。
2 默认的+
规则旨在匹配单词之间的 ,其中“单词”是字母的连续序列(Unicode类别L),数字(Nd)或下划线。在代码中,它指定为:
ws
regex ws { <!ww> \s* }
是“字内”测试。因此ww
的意思是“单词”内的 not 。 <!ww>
总是会在<ws>
会成功的地方取得成功,但与\s*
不同的是,它不会在一个单词的中间成功。 (就像其他用\s*
量化的原子一样,简单的*
总是匹配的,因为它匹配任意数量的空格,包括根本没有。)
答案 1 :(得分:5)
如果您不需要使用语法,则可以执行以下操作:
my $a = "39 3A 3B 9:;";
say $a.split(/\s+/).grep: * ~~ /<< <[0..9 A..F]> ** 2 >>/;
正则表达式将匹配这些两位数的六进制字符串。无论如何,您的语法问题可能在于您使用的空格数。在这种意义上,他们非常严格。