【问题标题】:Regex matching 2 or more nested optional terms匹配 2 个或更多嵌套可选术语的正则表达式
【发布时间】:2017-12-03 21:02:44
【问题描述】:

我想用 preg_match_all 解析这个字符串:

$str = "form.input|type()
        form.input|type('text')
        form.input|type('text', {'width': '100px', 'height': '50px'})
        form.input|type('image', {'path': '/path/to/image'})";

preg_match_all('/form\.input\|type\((?:(.*))?\)/', $str, $matches);

预期输出:

 [0] => Array
    (
        [0] => form.input|type()
        [1] => form.input|type('text')
        [2] => form.input|type('image', {'path': '/path/to/image'})
        [3] => form.input|type('text', {'width': '100px', 'height': '50px'})
    )

 [1] => Array
    (
        [0] => 
        [1] => text
        [2] => image
        [3] => text
    )

[2] => Array
    (
        [0] => 
        [1] => 
        [2] => {'path': '/path/to/image'}
        [3] => {'width': '100px', 'height': '50px'}
    )

实际输出:

Array
(
    [0] => Array
        (
            [0] => form.input|type()
            [1] => form.input|type('text')
            [2] => form.input|type('image', {'path': '/path/to/image'})
            [3] => form.input|type('text', {'width': '100px', 'height': '50px'})
        )

    [1] => Array
        (
            [0] => 
            [1] => 'text'
            [2] => 'image', {'path': '/path/to/image'}
            [3] => 'text', {'width': '100px', 'height': '50px'}
        )

)

这个模式可以解析案例:

form.input|type()
form.input|type('text')

我尝试通过这种模式进行匹配:

/form\.input\|type\((?:(.*)(?:,(.*))?)?\)/

但由于子非捕获组,无法匹配模式。

我使用非捕获组(?:(.*))? 进行可选匹配,但只有在没有使用第一个模式的子非捕获组时才能匹配。

我试图搜索这种情况下的匹配,但我找不到正确的答案。

【问题讨论】:

  • 你期望输出什么?您的模式匹配上述所有情况:regex101.com/r/JLmX3Q/1 甚至 \Qform.input|type(\E(?:([^()]*))?\)
  • @Jan 我用预期的输出编辑了这个问题。此模式将 type() 中的所有内容匹配为一个字符串。但我想将它们分成两部分进行匹配:字符串类型和 json 选项。
  • 了解您不想匹配的内容会很有帮助。从您的示例来看,这已经足够了/f.+/
  • @miknik 你能在例子中应用它吗? /form\.input\|type((?:(.*))?)/
  • @semsem 您的问题是“我想使用 preg_match 解析这个字符串”这对我来说有点不清楚。您是否使用preg_match_all() 从每一行中提取值?或者这实际上是四个单独的示例字符串,您想在所有四个上调用preg_match()?请将您的 php 实现添加到问题中以阐明实际任务?您想每次返回 2 个捕获组吗?即使其中一个或两个捕获组为空?这些都是必须包含在您的问题中的关键细节。

标签: php regex preg-match-all


【解决方案1】:

这是我建议的处理单引号和双引号的模式:(Pattern Demo)

/form\.input\|type\(['"]?([a-z]*)['"]?(?:, )?([^)]*)/

模式说明:

form\.input\|type\(  // Literally match the static/known leading characters
['"]?                // optionally match a single or double quote
([a-z]*)             // greedily capture zero or more lowercase letters
['"]?                // optionally match a single or double quote
(?:, )?              // optionally match a comma followed by a space
([^)]*)              // greedily capture zero or more non-closing parenthesis characters

有效地,通过使用“零或一”(?)或“零或多个”(*)量词,字符串可以有空或非空括号组件,并确保两个预期的捕获组都以输出数组。

PHP 代码:(Demo)

$str = "form.input|type()   
        form.input|type('text')
        form.input|type(\"text\", {'width': '100px', 'height': '50px'})
        form.input|type('image', {'path': '/path/to/image'})";

print_r(preg_match_all("/form\.input\|type\(['\"]?([a-z]*)['\"]?(?:, )?([^)]*)/",$str,$out)?array_slice($out,1):'fail');

输出:

Array
(
    [0] => Array
        (
            [0] => 
            [1] => text
            [2] => text
            [3] => image
        )

    [1] => Array
        (
            [0] => 
            [1] => 
            [2] => {'width': '100px', 'height': '50px'}
            [3] => {'path': '/path/to/image'}
        )

)

【讨论】:

    【解决方案2】:

    您可以使用正则表达式和explode() 的组合:

    <?php
    
    $strings = ["form.input|type()","form.input|type('text')","form.input|type('text', {'width': '100px', 'height': '50px'})", "form.input|type('image', {'path': '/path/to/image'})']"];
    
    $regex = '~\(([^()]+)\)~';
    
    foreach ($strings as $string) {
        if (preg_match($regex, $string, $match)) {
            list($key, $value) = explode(", ", $match[1], 1);
            echo $key, $value . "\n";
        }
    }
    ?>
    

    a demo on ideone.com


    这里的想法是在() 中寻找一些东西,然后在找到的第一个, 上拆分/分解。如果要将表达式限制为 form.input,可以将表达式更改为:
    \Qform.input|type\E\(([^()]+)\)
    

    a demo on regex101.com


    附录

    要去掉引号,可以使用

    <?php
    
    $strings = ["form.input|type()",
                            "form.input|type('text')",
                            "form.input|type('text', {'width': '100px', 'height': '50px'})", 
                            "form.input|type('image', {'path': '/path/to/image'})']",
                            "form.input|type(\"image\", {'path': '/path/to/image2'})']"];
    
    
    $regex = '~\(([^()]+)\)~';
    $key_value = '~^([\'"])(.+?)\1(?:, )?(.*)~';
    
    foreach ($strings as $string) {
        if (preg_match($regex, $string, $match)) {
            if (preg_match($key_value, $match[1], $inner)) {
                $key = $inner[2];
                $value = $inner[3];
                echo "Key = $key, Value = $value\n";
            }
        }
    }
    ?>
    

    产量

    Key = text, Value = 
    Key = text, Value = {'width': '100px', 'height': '50px'}
    Key = image, Value = {'path': '/path/to/image'}
    Key = image, Value = {'path': '/path/to/image2'}
    

    【讨论】:

    • 谢谢@Jan,有没有比替换它们更好的方法来删除匹配的输出字符串中的单引号和双引号? “文本”和“文本”
    【解决方案3】:

    这个

    preg_match_all('/((?<=\(\')\w+)|({.*})/', $input, $matches, PREG_PATTERN_ORDER, 0);
    
    print_r($matches);
    

    会返回这个:

    Array
    (
    [0] => Array
    (
    [0] => text
    [1] => {'width': '100px', 'height': '50px'}
    )
    

    【讨论】:

    • 请永远不要发布纯代码的答案——永远不要。他们可能会解决问题,但价值不高,对教育未来的读者几乎没有作用。将 SO 视为拥有数千名学生的教室。请更新您的答案。
    • @semsem 这个答案不正确,对吧?因为它不会从第一个示例字符串中返回任何匹配项。问题中没有 php 代码,因此我们不知道您是否要每次返回 2 个匹配项(可能为空),或者您是否将不匹配项作为第一个示例字符串的有效结果来处理。 (form.input|type()) 请说明您的 4 个样本输入的预期输出数组。
    • @mickmackusa 这个答案不准确。对不起,我将编辑问题以用 php 代码澄清。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多