如何在Perl6语法中匹配十六进制数组

tej*_*jas 12 regex grammar perl6

我有一个类似的字符串"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);
Run Code Online (Sandbox Code Playgroud)

但这似乎不起作用。甚至这似乎也不起作用。

my $a = "39 3A 3B ";
grammar Hex {
    token TOP { <hex_array>+ }
    token hex_array { <[0..9 A..F]> " " }
};
Hex.parse($a);
Run Code Online (Sandbox Code Playgroud)

我确实尝试过语法:: Tracer TOP和hex_array均失败

TOP
|  hex_array
|  * FAIL
* FAIL
Run Code Online (Sandbox Code Playgroud)

rai*_*iph 14

<[abcdef...]>在P6正则表达式是“字符类”中的匹配- 一个字符数限制的意义。1个

惯用的方式来获得你想要的是使用**量词

my $a = "39 3A 3B ";
grammar Hex {
  token TOP { <hex_array>+ }
  token hex_array { <[0..9 A..F]>**1..2 " " }
};
Hex.parse($a);
Run Code Online (Sandbox Code Playgroud)

该答案的其余部分是有关为什么以及如何使用rules的“奖励”材料。

您当然可以完全自由地匹配空白情况,方法是将空白模式包含在任意单独的标记中,就像您" "hex_array标记中所做的那样。

但是,在大多数情况下,最好在适当rule时候使用s代替。

首先,使用ws代替“” \s*等。

让我们删除第二个空格,token然后将其移动到第一个空格:

  token TOP { [ <hex_array> " " ]+ }
  token hex_array { <[0..9 A..F]>**1..2 }
Run Code Online (Sandbox Code Playgroud)

我们添加了方括号([...]),将hex_array和和一个空格组合在一起,然后将+量词应用于该组合的原子。这是一个简单的更改,语法继续像以前一样工作,与以前一样匹配空格,但是现在空格不会被hex_array令牌捕获。

接下来,让我们切换到使用内置的:ws token

  token TOP { [ <hex_array> <.ws> ]+ }
Run Code Online (Sandbox Code Playgroud)

<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
Run Code Online (Sandbox Code Playgroud)

不使用 :sigspace(默认情况下,在tokens和regexs中),将忽略模式中的所有文字空间(除非您引用它们)。这对于单个s的可读模式通常是理想的,因为它们通常指定要匹配的文字内容。token

但是一旦:sigspace生效,原子后的空格变得“重要”,因为它们被隐式转换为<.ws>[ ... <.ws> ]调用。这对于指定标记或子规则序列的可读模式是理想的,因为这是避免所有这些额外调用混乱的自然方法。

下面的第一个模式将匹配一个或多个hex_array令牌,它们之间或末尾没有空格匹配。最后两个将匹配一个或多个hex_arrays,不插入空格,然后在末尾添加或不添加空格:

  token TOP {           <hex_array>+ }
  #          ^ ignored ^            ^ ignored

  token TOP { :sigspace <hex_array>+ }
  #          ^ ignored ^            ^ significant

  rule TOP  {           <hex_array>+ }
  #          ^ ignored ^            ^ significant
Run Code Online (Sandbox Code Playgroud)

注意 副词(如:sigspace)不是原子。第一个原子之前的空间(在上面,在之前的 空间<hex_array>永远不会有意义(无论是否:sigspace有效)。但是此后,如果:sigspace有效,则该模式中所有未引用的空格都是“有效的”,即将其转换为<.ws>[ ... <.ws> ]

在上面的代码中,第二个标记和规则将单个字符 hex_array与后面的空格匹配,因为紧接着手段+之前和之后的空格}将模式重写为:

  token TOP { <hex_array>+ <.ws> }
Run Code Online (Sandbox Code Playgroud)

但是,如果您的输入包含多个标记,并且它们之间有一个或多个空格则此重写的标记将不匹配。相反,您将要编写: hex_array

  rule TOP { <hex_array> + }
  # ignored ^           ^ ^ both these spaces are significant
Run Code Online (Sandbox Code Playgroud)

改写为:

  token TOP { [ <hex_array> <.ws> ]+ <.ws> }
Run Code Online (Sandbox Code Playgroud)

这将匹配您的输入。

结论

因此,在经历了所有明显的复杂性之后(实际上,这只是我的详尽描述),我建议您将原始代码编写为:

my $a = "39 3A 3B ";
grammar Hex {
  rule TOP { <hex_array> + }
  token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
Run Code Online (Sandbox Code Playgroud)

而且这将比您的原始版本更灵活地匹配(我认为这将是一件好事,尽管对于某些用例当然不会),并且对于大多数P6er来说可能更易于阅读。

最后,要加强如何避免rules 的三个杂项中的两个,请参见在perl6语法中放宽空白的最佳方法是什么?。(第三个问题是,是否需要在原子和量词之间放置一个空格,就像上面的the <hex_array>和the 之间的空格一样+。)

脚注

1如果要匹配多个字符,则将适当的量词附加到字符类。根据Wikipedia的说法,这是明智的处理方式,也是“字符类”的假定行为。不幸的是,P6文档当前使该问题感到困惑,例如将真正的字符类和与多个字符匹配的其他规则(在“ 预定义字符类 ”标题下)结合在一起。

2默认ws规则旨在匹配单词之间,其中“单词”是字母(Unicode类别L),数字(Nd)或下划线的连续序列。在代码中,它指定为:

regex ws { <!ww> \s* }
Run Code Online (Sandbox Code Playgroud)

ww是“字内”测试。因此,<!ww>意味着不在 “单词”之内。<ws>总是会在\s*可能的地方成功-唯一的不同是\s*,它不会在单词中间成功。(就像其他任何用a量化的原子一样*,平原\s*将始终匹配,因为它匹配任意数量的空间,包括根本不匹配的空间。)


jjm*_*elo 5

如果您不需要使用语法,则可以执行以下操作:

my $a = "39 3A 3B  9:;";
say $a.split(/\s+/).grep: * ~~ /<< <[0..9 A..F]> ** 2 >>/;
Run Code Online (Sandbox Code Playgroud)

正则表达式将匹配这些两位数的六进制字符串。无论如何,您的语法问题可能在于您使用的空格数。从这个意义上讲,他们非常严格