如何使用preg_match_all()获取子组匹配的所有捕获?

时间:2011-06-16 11:41:34

标签: php regex grammar preg-match-all

更新/注意:

  

我认为我可能正在寻找的是在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_...系列函数返回所有子组匹配。

对于真实案例,假设您有多个(嵌套)级别的子模式匹配变量。

实施例

这是伪代码中用来描述背景的一个例子。 想象以下内容:

令牌的常规定义:

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

$subject根据这些进行标记。标记化存储在一个标记数组(type,offset,...)中。

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

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

因此,现在可以在令牌流字符串索引上运行基于令牌(而不是字符类等)的正则表达式。 E.g。

   regex: (cs)?cp

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

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

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

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

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

我可以编码直到这一点。然后我遇到了子组匹配的问题只包含他们的最后一场比赛。

所以我可以选择自己为语法创建一个自动机(我想阻止它保持语法表达式通用)或者稍微让preg_match以某种方式为我工作,所以我可以节省它。

基本上都是这样。可能现在,为什么我简化了这个问题是可以理解的。


相关:

8 个答案:

答案 0 :(得分:3)

试试这个:

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
        )

)

答案 1 :(得分:3)

类似帖子:Get repeated matches with preg_match_all()

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

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

  

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

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

修改

我看到其他帖子someone声称lookbehind方法能够做到。

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分:1)

  

有没有办法可以通过一次正则表达式执行来检索所有匹配项(AA,BB,DD)? preg_match_all不适合这个吗?

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

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

每条评论,我提到的红宝石正则表达式:

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");

但我认为你需要一个lexer / parser / tokenizer来在PHP中做同样的事情。 : - |

答案 4 :(得分:0)

修改

我没有意识到你原来要求的是什么。这是新的解决方案:

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

答案 5 :(得分:0)

怎么样:

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

输出

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

答案 6 :(得分:0)

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

// 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;

答案 7 :(得分:0)

是的,你的解决方案是正确的,使用preg_match_all preg_match_all是递归的,所以不要使用start-with ^和end-with $,以便preg_match_all全部放入在阵列中找到了模式。

每对新的括号将添加一个表示不同匹配的新数组

使用?进行可选匹配

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

需要澄清

让我试着理解你的问题,以便我的回答符合你的要求。

  1. 您的$subject不是您所寻找的好例子吗?

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

  3. 你想要返回的匹配,是否指定了匹配的偏移量?

  4. $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
            )
    
    )