【问题标题】:How do I access the captures within a match?如何访问比赛中的捕获?
【发布时间】:2016-11-24 07:06:04
【问题描述】:

我正在尝试解析 csv 文件,并且正在尝试访问 Perl6 中原始正则表达式中的名称正则表达式。结果是零。正确的做法是什么?

grammar rsCSV {
    regex TOP { ( \s* <oneCSV> \s* \, \s* )* }
    proto regex oneCSV {*}
          regex oneCSV:sym<noQuote> { <-[\"]>*?  }
          regex oneCSV:sym<quoted>  { \" .*? \" } # use non-greedy match
}

my $input = prompt("Enter csv line: "); 

my $m1 = rsCSV.parse($input);
say "===========================";
say $m1;
say "===========================";
say "1 " ~ $m1<oneCSV><quoted>;  # this fails; it is "Nil"
say "2 " ~ $m1[0];
say "3 " ~ $m1[0][2];

【问题讨论】:

    标签: regex match raku named


    【解决方案1】:

    补充克里斯托夫回答的详细讨论

    我正在尝试解析一个 csv 文件

    也许您专注于学习 Perl 6 解析并且正在编写一些一次性代码。但是,如果您想要开箱即用的工业级 CSV 解析,请注意 Text::CSV 模块[1]

    我正在尝试访问一个命名的正则表达式

    如果您正在学习 Perl 6 解析,请注意 jnthn 的语法跟踪器和调试器[2]

    在 Perl6 的原始正则表达式中

    您的问题与它是原始正则表达式无关。

    问题在于,虽然与您命名的捕获相对应的匹配对象存储在您存储在$m1 中的整体匹配对象中,但它并未准确存储在您要查找的位置.

    捕获对应的匹配对象出现在哪里?

    要查看发生了什么,我将从模拟您尝试执行的操作开始。我将使用一个仅声明一个捕获的正则表达式,即与字符串 ab 匹配的“命名”(也称为“关联”)捕获。

    given 'ab'
    {
        my $m1 = m/ $<named-capture> = ( ab ) /;
    
        say $m1<named-capture>;
        # 「ab」
    }
    

    与命名捕获对应的匹配对象存储在您可能希望它出现在$m1 中的位置$m1&lt;named-capture&gt;

    但是你得到了 $m1&lt;oneCSV&gt; 的 Nil。什么给了?

    为什么你的$m1&lt;oneCSV&gt; 不起作用

    有两种类型的捕获:命名(又名“关联”)和编号(又名“位置”)。您在正则表达式中围绕&lt;oneCSV&gt; 编写的括号引入了编号捕获:

    given 'ab'
    {
        my $m1 = m/ ( $<named-capture> = ( ab ) ) /; # extra parens added
    
        say $m1[0]<named-capture>;
        # 「ab」
    }
    

    / ( ... ) / 中的括号声明了一个顶级编号的捕获。如果匹配,则对应的匹配对象存储在$m1[0]。 (如果您的正则表达式看起来像/ ... ( ... ) ... ( ... ) ... ( ... ) ... /,那么与第二对括号匹配的另一个匹配对象将存储在$m1[1] 中,另一个在$m1[2] 中存储第三个,依此类推。)

    $&lt;named-capture&gt; = ( ab ) 的匹配结果随后存储在内部 $m1[0]。这就是 say $m1[0]&lt;named-capture&gt; 起作用的原因。

    到目前为止一切顺利。但这只是故事的一半......

    为什么您的代码中的$m1[0]&lt;oneCSV&gt; 也不起作用

    虽然上面代码中的$m1[0]&lt;named-capture&gt; 正常工作,但您将仍然在原始代码中的$m1[0]&lt;oneCSV&gt; 中找不到匹配对象。这是因为您还请求了第零次捕获的多个匹配,因为您使用了 * quantifier

    given 'ab'
    {
        my $m1 = m/ ( $<named-capture> = ( ab ) )* /; # * is a quantifier
    
        say $m1[0][0]<named-capture>;
        # 「ab」
    }
    

    因为* 量词要求多个匹配,Perl 6 将匹配对象的列表 写入$m1[0]。 (在这种情况下,只有一个这样的匹配项,因此您最终会得到一个长度为 1 的列表,即只有 $m1[0][0](而不是 $m1[0][1]$m1[0][2] 等)。)

    总结

    • 捕捉巢穴;

    • *+ 量化的捕获对应于两个 级的嵌套,而不仅仅是一个。

    • 在您的原始代码中,您必须编写 say $m1[0][0]&lt;oneCSV&gt;; 才能找到您正在寻找的匹配对象。


    [1] 安装相关模块并在代码开头写上use Text::CSV;(用于纯Perl 6 实现)或use Text::CSV:from&lt;Perl5&gt;;(用于Perl 5 plus XS 实现)。 (talk slides(单击最上面的单词,例如“csv”,以浏览幻灯片),videoPerl 6 modulePerl 5 XS module。)

    [2] 安装相关模块并在代码开头写use Grammar::Tracer;use Grammar::Debugger;`。 (talk slides, video, modules.)

    【讨论】:

    • 非常感谢,raiph !!!经过您的详细解释,我现在看到了我的问题。非常感谢您的宝贵时间!!!
    • @lisprogtor 不客气。如果你能告诉我哪些特定的部分对你最有帮助,那对我特别有帮助。 :)
    • 谢谢 raiph。这是您对命名/编号捕获和匹配对象树之间关联的解释,以及使用 *,perl6 构造一个列表而不是单个对象这一事实。再次感谢!
    【解决方案2】:

    &lt;oneCSV&gt; 的匹配项位于捕获组的范围内,您可以通过 $m1[0] 获得。

    由于组是用* 量化的,结果将再次成为一个列表,即您需要另一个索引操作来获取匹配对象,例如$m1[0][0] 是第一个。

    然后可以通过名称访问命名的捕获,例如$m1[0][0]&lt;oneCSV&gt;。这将已经包含 protoregex 的适当分支的匹配结果。

    如果您想要整个匹配列表而不是特定匹配,您可以使用&gt;&gt;map,例如$m1[0]&gt;&gt;.&lt;oneCSV&gt;

    【讨论】:

    • 谢谢克里斯托夫。您的回答加深了我对 Perl 6 的理解!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 2020-01-22
    • 2012-02-29
    相关资源
    最近更新 更多