【问题标题】:How to get all captures of subgroup matches with preg_match_all()? [duplicate]如何使用 preg_match_all() 获取子组匹配的所有捕获? [复制]
【发布时间】:2011-09-16 07:21:00
【问题描述】:

更新/注意:

我想我可能正在寻找的是在 PHP 中获取 captures of a group

参考:PCRE regular expressions using named pattern subroutines.

(仔细阅读:)


我有一个包含可变数量段的字符串(简化):

$subject = 'AA BB DD '; // could be 'AA BB DD CC EE ' as well

我现在想匹配段并通过匹配数组返回它们:

$pattern = '/^(([a-z]+) )+$/i';
$result = preg_match_all($pattern, $subject, $matches);

这将只返回捕获组 2 的 last 匹配项:DD

有没有一种方法可以通过一次正则表达式执行检索所有子模式捕获(AABBDD)? preg_match_all不适合这个吗?

这个问题是一个概括性的问题。

$subject$pattern 均已简化。自然,有了AABB、.. 这样的一般列表,使用其他函数(例如explode)或$pattern 的变体更容易提取。

但我特意询问如何使用preg_...-family 函数返回所有子组匹配项。

对于现实生活中的案例,假设您有多个(嵌套)级别的不同数量的子模式匹配。

示例

这是一个伪代码示例,用于描述一些背景。 想象一下

令牌的常规定义:

   CHARS := [a-z]+
   PUNCT := [.,!?]
   WS := [ ]

$subject 得到基于这些的标记。标记化存储在标记数组中(类型、偏移量、...)。

然后将该数组转换为一个字符串,每个标记包含一个字符:

   CHARS -> "c"
   PUNCT -> "p"
   WS -> "s"

现在可以在令牌流字符串索引上运行基于令牌(而不是字符类等)的正则表达式。例如

   regex: (cs)?cp

表示一组或多组字符,后跟标点符号。

因为我现在可以将自定义标记表示为正则表达式,所以下一步是构建语法。这只是一个例子,这是一种 ABNF 风格:

   words = word | (word space)+ word
   word = CHARS+
   space = WS
   punctuation = PUNCT

如果我现在将 words 的语法编译成(令牌)正则表达式,我自然希望每个 word 的所有子组匹配。

  words = (CHARS+) | ( (CHARS+) WS )+ (CHARS+)    # words resolved to tokens
  words = (c+)|((c+)s)+c+                         # words resolved to regex

到目前为止,我可以编写代码。然后我遇到了子组比赛只包含他们最后一场比赛的问题。

所以我可以选择自己为语法创建一个自动机(我想阻止它以保持语法表达式的通用性),或者在某种程度上让 preg_match 以某种方式为我工作,这样我就可以省去它了。

基本上就是这样。或许现在可以理解我为什么简化问题了。


相关:

【问题讨论】:

  • 如果您将问题概括得如此之多,以至于可以给出正确答案的替代方案,那么您的问题就没有那么有价值了。如果您不想要简化的答案,请不要简化。 -1.
  • 我正在寻找有关特定主题的答案。我不明白为什么简化对于使其可见是不好的,尽管我看到一定程度的抽象可能是一种负担。
  • 嗯,很明显,因为您想要一个子组的答案,而您的示例不包括对子组的需求。这个例子有缺陷。
  • @Berry Langerak:简化总是有一些损失。您会发现现在添加了一个更详细的示例。
  • 刚刚偶然发现:J (PCRE_INFO_JCHANGED) - (?J) 内部选项设置更改了本地 PCRE_DUPNAMES 选项。允许子模式的重复名称在这里可能无法解决此问题,但通常很有趣:php.net/manual/en/reference.pcre.pattern.modifiers.php

标签: php regex grammar preg-match-all


【解决方案1】:

类似线程:Get repeated matches with preg_match_all()

检查选择的答案加上我的可能有用我会在那里复制:

来自http://www.php.net/manual/en/regexp.reference.repetition.php

当重复捕获子模式时,捕获的值是匹配最终迭代的子字符串。

我个人放弃并打算分两步完成。

编辑

我在另一个线程中看到someone 声称lookbehind 方法能够做到这一点。

【讨论】:

    【解决方案2】:

    是的,你的权利你的解决方案是使用 preg_match_all preg_match_all 是递归的,所以不要使用 start-with^ 和 end-with$,以便 preg_match_all 将所有找到的模式放在一个数组中。

    每对新的括号都会添加一个新的数组来指示不同的匹配项

    使用? 进行可选匹配

    您可以用括号 () 分隔报告的不同模式组,以要求找到一个组并将其添加到新数组中(可以让您计算匹配项,或从返回的数组中对每个匹配项进行分类)

    需要说明

    让我试着理解你的问题,以便我的回答与你所问的相符。

    1. 您的$subject 不是您正在寻找的一个很好的例子?

    2. 您希望预匹配搜索,将您在 $subject 中提供的内容拆分为 4 个类别单词字符标点符号空格?那么数字呢?

    3. 您还希望返回的匹配项指定匹配项的偏移量吗?

    $subject = 'aa.bb cc.dd EE FFF,GG'; 是否更适合现实生活中的例子?

    我将在$subject 中采用您的基本示例,并使其能够准确地满足您的要求。

    所以你能编辑你的$subject 以便我更适合你想要匹配的所有案例

    '/^(([a-z]+) )+$/i';

    随时通知我, 你可以在这里测试你的正则表达式http://www.spaweditor.com/scripts/regex/index.php

    部分回答

    /([a-z])([a-z]+)/i

    AA BB DD CD

    Array
    (
        [0] => Array
            (
                [0] => AA
                [1] => BB
                [2] => DD
                [3] => CD
            )
    
        [1] => Array
            (
                [0] => A
                [1] => B
                [2] => D
                [3] => C
            )
    
        [2] => Array
            (
                [0] => A
                [1] => B
                [2] => D
                [3] => D
            )
    
    )
    

    【讨论】:

    • 不,这不是解决方案。您的示例甚至无法验证整个字符串是否与正则表达式匹配,您只是将问题转移到字符串的子集而不是整个字符串。子组及其所有匹配/捕获在哪里?
    • 我想运行 preg_match_all 并想获取所有子组捕获,而不仅仅是最后一个。
    • @hakre 有 2 1/2 类型的子组,因为您的正则表达式有缺陷。所有正确的答案都是错误的,我们不知道你想要什么样的结果,给我们一个你想要的结果数组的例子。
    • ((a)(b)){2}) => 返回两个外部组匹配,返回两个内部组匹配,然后存在两个 次例如。这个例子也可以是一个子组,而不仅仅是整个模式。 AFAIK 一次性使用 PHP 的正则表达式引擎是不可能的。
    • 我应该将我在问题中给出的示例放入代码中,以便它的抽象字符获得更多“类似动手”的表示。这可能会有所帮助。
    【解决方案3】:

    有没有一种方法可以通过一次正则表达式检索来检索所有匹配项(AA、BB、DD)?是不是 preg_match_all 不适合这个?

    您当前的正则表达式似乎用于 preg_match() 调用。试试这个:

    $pattern = '/[a-z]+/i';
    $result = preg_match_all($pattern, $subject, $matches);
    

    每个 cmets,我提到的 ruby​​ 正则表达式:

    sentence = %r{
    (?<subject>   cat   | dog        ){0}
    (?<verb>      eats  | drinks     ){0}
    (?<object>    water | bones      ){0}
    (?<adjective> big   | smelly     ){0}
    (?<obj_adj>   (\g<adjective>\s)? ){0}
    The\s\g<obj_adj>\g<subject>\s\g<verb>\s\g<opt_adj>\g<object>
    }x
    
    md = sentence.match("The cat drinks water");
    md = sentence.match("The big dog eats smelly bones");
    

    但我认为你需要一个词法分析器/解析器/标记器来在 PHP 中做同样的事情。 :-|

    【讨论】:

    • 请阅读最后更长的示例。我真的在研究完全匹配的子组模式匹配,这样我就不用为组和 BNF 语法的重复编写解析器了。因此,我需要所有(子)匹配,同时消耗整个主题。 preg_match_all 将始终从它的子模式返回最后一个匹配项,当它们可以重复时。
    • 我认为你想要做的是可以通过命名组和递归正则表达式来实现,但我不确定 PHP 是否支持后者。不过,您也许可以在 ruby​​ 中管理它。
    • 今晚我会仔细研究一下。
    • 顺便说一句,在我的上述建议中,这样做的想法有什么问题:$pattern = '/regex1|regex2/'?可以说,您需要测试每个标点符号,但至少它们会被正确拆分,并且单个单词/标点组将被提取,不是吗?
    • 不,因为它是语法:每个单词至少有一个组,并且单词的语义一起形成语法的下一个单词。所以它是堆叠的。并且在这些堆栈中具有可选的重复。因此,如果我只能获取比赛的数据,那就完美了。但是它只返回最后一个反向引用。即使在正则表达式执行之后,有一堆反向引用也会很酷。
    【解决方案4】:

    我可能误解了您所描述的内容。您是否只是在寻找中间有空格的字母组的模式?

    // any subject containing words:
    $subject = 'AfdfdfdA BdfdfdB DdD'; 
    $subject = 'AA BB CC';
    $subject = 'Af df dfdA Bdf dfdB DdD';
    
    $pattern = '/(([a-z]+)\s)+[a-z]+/i';
    
    $result = preg_match_all($pattern, $subject, $matches);
    print_r($matches);
    echo "<br/>";
    print_r($matches[0]);  // this matches $subject
    echo "<br/>".$result;
    

    【讨论】:

      【解决方案5】:

      怎么样:

      $str = 'AA BB CC';
      $arr = preg_split('/\s+/', $str);
      print_r($arr);
      

      输出:

      (
          [0] => AA
          [1] => BB
          [2] => CC
      )
      

      【讨论】:

        【解决方案6】:

        试试这个:

        preg_match_all("'[^ ]+'i",$text,$n);
        

        $n[0] 将包含文本中所有非空格字符组的数组。

        编辑:带有子组:

        preg_match_all("'([^ ]+)'i",$text,$n);
        

        现在$n[1] 将包含与$n[0] 完全相同的子组匹配项。这其实是没有意义的。

        Edit2: 嵌套子组示例:

        $test = "Hello I'm Joe! Hi I'm Jane!";
        preg_match_all("/(H(ello|i)) I'm (.*?)!/i",$test,$n);
        

        结果:

        Array
        (
            [0] => Array
                (
                    [0] => Hello I'm Joe!
                    [1] => Hi I'm Jane!
                )
        
            [1] => Array
                (
                    [0] => Hello
                    [1] => Hi
                )
        
            [2] => Array
                (
                    [0] => ello
                    [1] => i
                )
        
            [3] => Array
                (
                    [0] => Joe
                    [1] => Jane
                )
        
        )
        

        【讨论】:

        • 我对子组匹配的变体数量的匹配感兴趣。您的正则表达式没有任何子组。
        • 那么我不明白你的问题。您要求的匹配不需要子组。
        • 不只是你不明白这个问题。这个问题是完全错误的,因为 Hakre 无法解释自己。 -1 代表问题
        • 我添加了更多信息以显示它具有一定程度的抽象/概括。
        【解决方案7】:

        您无法提取子模式,因为您编写正则表达式的方式仅返回一个匹配项(同时使用 ^$,并在主模式上使用 +)。

        如果你这样写,你会看到你的子组是正确的:

        $pattern = '/(([a-z]+) )/i';
        

        (这仍然有一组不必要的括号,我只是把它留在那里以作说明)

        【讨论】:

        • 是否可以让表达式总是消耗整个主题?
        • @hakre 我的正则表达式?是的,它会。它将返回与规则匹配的所有模式。其实'/([a-z]+) /i' 应该够用了。
        • 当我在主题末尾添加# 时,它会返回匹配项,尽管它不会消耗整个$subject。我在我的模式中添加了开始和结束标记,因为我想将它扩展到 $subject 的全部内容。
        • @hakre 当# 被添加到字符串的末尾时,你想发生什么?您的模式会消耗整个字符串,# 将不会被匹配。如果需要匹配,则需要不同的正则表达式。请说明你到底想要什么。
        • @hakre 可能您可以将整个字符串与preg_match() 匹配,如果没问题,运行preg_match_all() 以提取值。
        【解决方案8】:

        编辑

        我没有意识到你最初的要求。这是新的解决方案:

        $result = preg_match_all('/[a-z]+/i', $subject, $matches);
        $resultArr = ($result) ? $matches[0] : array();
        

        【讨论】:

        • 那个正则表达式没有任何子组。我专门寻找子组的匹配项。
        猜你喜欢
        • 2011-09-28
        • 2016-04-05
        • 2021-06-21
        • 2011-03-09
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        相关资源
        最近更新 更多