gpt4 book ai didi

PHP Regex preg_match提取

转载 作者:可可西里 更新时间:2023-11-01 13:41:33 24 4
gpt4 key购买 nike

尽管我对伪代码中的正则表达式有足够的了解,但是我仍然无法翻译我想在php regex perl中执行的操作。
我正在尝试使用preg_match提取表达式的一部分。
我有以下字符串${classA.methodA.methodB(classB.methodC(classB.methodD)))},我需要做2件事:

一种。验证语法

  • ${classA.methodA.methodB(classB.methodC(classB.methodD)))} 有效
  • ${classA.methodA.methodB} 有效
  • ${classA.methodA.methodB()} 无效
  • ${methodB(methodC(classB.methodD)))} 无效

  • b。我需要提取这些信息 ${classA.methodA.methodB(classB.methodC(classB.methodD)))}应该返回

    1. A类
    2. methodA
    3. methodB(classB.methodC(classB.methodD)))

    我已经创建了这段代码
    $expression = '${myvalue.fdsfs.fsdf.blo(fsdf.fsfds(fsfs.fs))}';
    $pattern = '/\$\{(?:([a-zA-Z0-9]+)\.)(?:([a-zA-Z\d]+)\.)*([a-zA-Z\d.()]+)\}/';
    if(preg_match($pattern, $expression, $matches))
    {
    echo 'found'.'<br/>';
    for($i = 0; $i < count($matches); $i++)
    echo $i." ".$matches[$i].'<br/>';
    }

    结果是:
    成立
    0 $ {myvalue.fdsfs.fsdf.blo(fsdf.fsfds(fsfs.fs))}
    1 myvalue
    2 fsdf
    3 blo(fsdf.fsfds(fsfs.fs))

    显然,我很难提取重复的方​​法,并且无法正确验证它(老实说,一旦我解决了另一个问题,我就把它留在了最后),因此允许使用空括号,并且不检查是否在打开括号后就可以了它必须关闭。

    谢谢大家

    更新

    X m.buettner

    谢谢你的帮助。我对您的代码进行了快速尝试,但是尽管我可以忽略它,但它给出的问题很小。问题与我在此处未发布的先前代码之一相同,这是我尝试此字符串时的问题:
    $expression = '${myvalue.fdsfs}';

    使用您的模式定义,它显示:
    found
    0 ${myvalue.fdsfs}
    1 myvalue.fdsfs
    2 myvalue
    3
    4 fdsfs

    如您所见,第三行被捕获为不存在的空白。我不明白为什么这样做,所以您可以建议我怎么做,或者由于php regex的限制我必须忍受它吗?

    就是说,我只能告诉你,谢谢。您不仅回答了我的问题,而且还尝试输入尽可能多的信息,并提出了许多建议,以帮助您开发模式时应遵循的正确方法。

    我(愚蠢的)最后一件事忘记添加一个重要的小例子,这是多个参数除以逗号,所以
    $expression = '${classA.methodAA(classB.methodBA(classC.methodCA),classC.methodCB)}';
    $expression = '${classA.methodAA(classB.methodBA(classC.methodCA),classC.methodCB,classD.mehtodDA)}';

    必须是有效的。

    我对此进行了编辑
        $expressionPattern =             
    '/
    ^ # beginning of the string
    [$][{] # literal ${
    ( # group 1, used for recursion
    ( # group 2 (class name)
    [a-z\d]+ # one or more alphanumeric characters
    ) # end of group 2 (class name)
    [.] # literal .
    ( # group 3 (all intermediate method names)
    (?: # non-capturing group that matches a single method name
    [a-z\d]+ # one or more alphanumeric characters
    [.] # literal .
    )* # end of method name, repeat 0 or more times
    ) # end of group 3 (intermediate method names);
    ( # group 4 (final method name and arguments)
    [a-z\d]+ # one or or more alphanumeric characters
    (?: # non-capturing group for arguments
    [(] # literal (
    (?1) # recursively apply the pattern inside group 1
    (?: # non-capturing group for multiple arguments
    [,] # literal ,
    (?1) # recursively apply the pattern inside group 1 on parameters
    )* # end of multiple arguments group; repeat 0 or more times
    [)] # literal )
    )? # end of argument-group; make optional
    ) # end of group 4 (method name and arguments)
    ) # end of group 1 (recursion group)
    [}] # literal }
    $ # end of the string
    /ix';

    X卡西米尔和希波吕特

    您的建议也不错,但是使用此代码时可能会出现一些复杂的情况。我的意思是代码本身很容易理解,但灵活性却降低了。话虽如此,它也为我提供了很多信息,这些信息将来肯定会对您有所帮助。

    X Denomales

    感谢您的支持,但是当我尝试这样做时,您的代码就掉了:
    $sourcestring='${classA1.methodA0.methodA1.methodB1(classB.methodC(classB.methodD))}';

    结果是:
    Array

    (
    [0] =>数组
    (
    [0] => $ {classA1.methodA0.methodA1.methodB1(classB.methodC(classB.methodD))}
    )
    [1] => Array
    (
    [0] => classA1
    )

    [2] => Array
    (
    [0] => methodA0
    )

    [3] => Array
    (
    [0] => methodA1.methodB1(classB.methodC(classB.methodD))
    )

    )

    它应该是
        [2] => Array
    (
    [0] => methodA0.methodA1
    )

    [3] => Array
    (
    [0] => methodB1(classB.methodC(classB.methodD))
    )

    )

    或者
    [2] => Array
    (
    [0] => methodA0
    )

    [3] => Array
    (
    [0] => methodA1
    )

    [4] => Array
    (
    [0] => methodB1(classB.methodC(classB.methodD))
    )

    )

    最佳答案

    这是困难的一个。递归模式通常超出了正则表达式所能提供的范围,即使有可能,也会导致很难理解和维护的表达式变得非常困难。

    您正在使用PHP,因此使用的是PCRE,它实际上支持递归正则表达式构造(?n)。由于您的递归模式非常有规律,因此可以使用正则表达式找到一些实用的解决方案。

    我要立即提一个警告:由于您允许每个级别调用任意数量的“中间”方法(在代码段fdsfsfsdf中),因此您无法在单独的捕获中获得所有这些方法。使用PCRE根本不可能。每次匹配将始终产生相同数量的捕获,这取决于您的模式所包含的开括号数量。如果重复使用捕获组(例如,使用诸如([a-z]+\.)+之类的东西),则每次使用该捕获组时,先前的捕获都会被覆盖,并且您只会得到最后一个实例。因此,我建议您一起捕获所有“中间”方法调用,然后简单地将结果 explode 捕获。

    同样,您无法(如果想要)一次捕获多个嵌套级别。因此,您想要的捕获(最后一个包含所有嵌套级别)是唯一的选择-然后您可以将模式再次应用于最后一个匹配,以进一步降低级别。

    现在,对于实际表达式:

    $pattern = '/
    ^ # beginning of the string
    [$][{] # literal ${
    ( # group 1, used for recursion
    ( # group 2 (class name)
    [a-z\d]+ # one or more alphanumeric characters
    ) # end of group 2 (class name)
    [.] # literal .
    ( # group 3 (all intermediate method names)
    (?: # non-capturing group that matches a single method name
    [a-z\d]+ # one or more alphanumeric characters
    [.] # literal .
    )* # end of method name, repeat 0 or more times
    ) # end of group 3 (intermediate method names);
    ( # group 4 (final method name and arguments)
    [a-z\d]+ # one or or more alphanumeric characters
    (?: # non-capturing group for arguments
    [(] # literal (
    (?1) # recursively apply the pattern inside group 1
    [)] # literal )
    )? # end of argument-group; make optional
    ) # end of group 4 (method name and arguments)
    ) # end of group 1 (recursion group)
    [}] # literal }
    $ # end of the string
    /ix';

    一些一般性注意事项:对于复杂的表达式(以及支持它的正则表达式),请始终使用自由间距的 x修饰符,该修饰符允许您引入空格和注释,以根据需要设置表达式的格式。没有它们,模式将如下所示:
    '/^[$][{](([a-z\d]+)[.]((?:[a-z\d]+[.])*)([a-z\d]+(?:[(](?1)[)])?))[}]$/ix'

    即使您自己编写了正则表达式,并且您是唯一从事过该项目的人-尝试从现在开始的一个月都可以理解这一点。

    其次,我通过使用不区分大小写的 i修饰符略微简化了模式。它只是消除了一些困惑,因为您可以省略字母的大写变体。

    第三,请注意,在可能的情况下,我使用诸如 [$][.]的单字符类来转义字符。这只是一个品味问题,您可以自由使用反斜杠变体。我个人更喜欢字符类的可读性(而且我知道这里的其他人也不同意),所以我也想向您介绍这个选项。

    第四,我在您的模式周围添加了 anchor ,以便在 ${...}之外没有无效的语法。

    最后,递归如何工作? (?n)与反向引用 \n相似,因为它引用捕获组 n(通过从左到右打开括号来计数)。区别在于,反向引用尝试再次匹配组 n匹配的内容,而 (?n)再次应用该模式。也就是说 (.)\1连续两次匹配任何字符,而 (.)(?1)匹配任何字符,然后再次应用模式,因此匹配了另一个任意字符。如果您在第 (?n)组中使用这些 n构造之一,则会获得递归。 (?0)(?R)指的是整个模式。那就是所有的魔力。

    上面的模式应用于输入
     '${abc.def.ghi.jkl(mno.pqr(stu.vwx))}'

    将导致捕获
    0 ${abc.def.ghi.jkl(mno.pqr(stu.vwx))}
    1 abc.def.ghi.jkl(mno.pqr(stu.vwx))
    2 abc
    3 def.ghi.
    4 jkl(mno.pqr(stu.vwx))

    请注意,您实际期望的输出有一些差异:
    0是整个匹配项(在这种情况下,仅是输入字符串)。 PHP将始终首先报告此问题,因此您无法摆脱它。
    1是第一个捕获该递归部分的捕获组。您不需要在输出中使用它,但是不幸的是 (?n)不能引用非捕获组,因此也需要它。
    2是所需的类名称。
    3是中间方法名称的列表,加上尾随句点。使用 explode可以很容易地从中提取所有方法名称。
    4是最终的方法名称,带有可选的(递归)参数列表。现在,您可以执行此操作,并在必要时再次应用该模式。请注意,对于完全递归的方法,您可能需要略微修改模式。那就是:在单独的第一步中剥离 ${},以便整个模式具有与最终捕获完全相同的(递归)模式,并且您可以使用 (?0)而不是 (?1)。然后匹配,删除方法名称和括号,然后重复,直到最后一次捕获中不再有括号为止。

    有关递归的更多信息,请查看 PHP's PCRE documentation

    为了说明我的最后一点,这是一个递归提取所有元素的代码段:
    if(!preg_match('/^[$][{](.*)[}]$/', $expression, $matches))
    echo 'Invalid syntax.';
    else
    traverseExpression($matches[1]);

    function traverseExpression($expression, $level = 0) {
    $pattern = '/^(([a-z\d]+)[.]((?:[a-z\d]+[.])*)([a-z\d]+(?:[(](?1)[)])?))$/i';
    if(preg_match($pattern, $expression, $matches)) {
    $indent = str_repeat(" ", 4*$level);
    echo $indent, "Class name: ", $matches[2], "<br />";
    foreach(explode(".", $matches[3], -1) as $method)
    echo $indent, "Method name: ", $method, "<br />";
    $parts = preg_split('/[()]/', $matches[4]);
    echo $indent, "Method name: ", $parts[0], "<br />";
    if(count($parts) > 1) {
    echo $indent, "With arguments:<br />";
    traverseExpression($parts[1], $level+1);
    }
    }
    else
    {
    echo 'Invalid syntax.';
    }
    }

    再次注意,我不建议将模式用作单线模式,但是此答案已经足够长了。

    关于PHP Regex preg_match提取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16987673/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com