我有一个像 "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);
我确实尝试过 Grammar::Tracer TOP 和 hex_array 都失败了
TOP
| hex_array
| * FAIL
* FAIL
最佳答案
<[abcdef...]>
在 P6 正则表达式中是匹配一个字符意义上的“字符类”。 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);
这个答案的其余部分是关于为什么和如何使用的“奖励” Material
rule
s。您当然可以完全自由地通过在任意单个标记中包含空白模式来匹配空白情况,就像您对
" "
所做的那样。在您的 hex_array
token 。但是,最好使用
rule
s而是在适当的时候——大多数时候是这样。首先,使用
ws
而不是“”,\s*
等。让我们删除第二个
token
中的空格并将其移至第一个: token TOP { [ <hex_array> " " ]+ }
token hex_array { <[0..9 A..F]>**1..2 }
我们添加了方括号 (
[...]
),它结合了 hex_array
和一个空格然后应用 +
该组合原子的量词。这是一个简单的更改,语法继续像以前一样工作,像以前一样匹配空格,只是现在空格不会被 hex_array
捕获。 token 。接下来,让我们切换到使用内置
ws
token
: token TOP { [ <hex_array> <.ws> ]+ }
默认
<ws>
以理想的方式比 \s*
更普遍有用.2 如果默认 ws
不做你需要的你可以指定你自己的 ws
token 。我们用过
<.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
s 和 regex
s 默认情况下),模式中的所有文字空格(除非你引用它们)都被忽略。这对于个人 token
的可读模式通常是可取的。 s 因为它们通常指定要匹配的文字内容。但曾经
:sigspace
有效,空格 后 原子变得“重要”——因为它们被隐式转换为 <.ws>
或 [ ... <.ws> ]
调用。这对于指定标记或子规则序列的可读模式是可取的,因为这是避免所有这些额外调用的困惑的自然方法。下面的第一个模式将匹配一个或多个
hex_array
在它们之间或末尾没有匹配空格的标记。最后两个将匹配一个或多个 hex_array
s,没有中间空格,然后在最后有或没有空格: token TOP { <hex_array>+ }
# ^ ignored ^ ^ ignored
token TOP { :sigspace <hex_array>+ }
# ^ ignored ^ ^ significant
rule TOP { <hex_array>+ }
# ^ ignored ^ ^ significant
注意。 副词(如
:sigspace
)不是原子。立即空格 之前 第一个原子(在上面, <hex_array>
之前的空格)是 从不 重要(无论 :sigspace
是否有效)。但此后,如果:sigspace
实际上,模式中所有未引用的空格都是“重要的”——也就是说,它被转换为 <.ws>
或 [ ... <.ws> ]
.在上面的代码中,第二个标记和规则将匹配单个
hex_array
后面有空格是因为 +
后面的空格和之前 }
意味着模式被重写为: token TOP { <hex_array>+ <.ws> }
但是如果您的输入有多个
hex_array
,这个重写的 token 将不匹配标记之间有一个或多个空格。相反,你会想写: rule TOP { <hex_array> + }
# ignored ^ ^ ^ both these spaces are significant
改写为:
token TOP { [ <hex_array> <.ws> ]+ <.ws> }
这将匹配您的输入。
结论
因此,在所有明显的复杂性之后,这实际上只是我非常精确,我建议您可以将原始代码编写为:
my $a = "39 3A 3B ";
grammar Hex {
rule TOP { <hex_array> + }
token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
并且这将比您的原始版本更灵活地匹配(我认为这将是一件好事,但当然它可能不适用于某些用例)并且对于大多数 P6ers 来说可能更容易阅读。
最后,强调如何避免
rule
的三个陷阱中的两个。 s,另见 What's the best way to be lax on whitespace in a perl6 grammar? . (第三个问题是你是否需要在原子和量词之间放置一个空格,就像上面的 <hex_array>
和 +
之间的空格一样。)脚注
1 如果要匹配多个字符,则在字符类中附加一个合适的量词。这是一种明智的做法,而且 the assumed behavior of a "character class" according to Wikipedia .不幸的是,P6 文档目前混淆了这个问题,例如将真正的字符类和其他匹配标题 Predefined character classes 下的多个字符的规则混在一起。 .
2 默认
ws
规则旨在匹配单词,其中“单词”是字母(Unicode 类别 L)、数字 (Nd) 或下划线的连续序列。在代码中,它被指定为:regex ws { <!ww> \s* }
ww
是一个“字内”测试。所以<!ww>
意思是不在一个“词”之内。 <ws>
总会成功的地方\s*
会——除了,不像 \s*
,它不会在一个词的中间成功。 (就像任何其他用 *
量化的原子一样,普通的 \s*
将始终匹配,因为它匹配任意数量的空格,包括根本不匹配。)
关于regex - 如何匹配 perl6 语法中的十六进制数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56421909/