如何更改我的正则表达式,以便preg_match返回所需的字符串

时间:2017-07-07 23:35:37

标签: php regex preg-match

我有字符串:

<mml:mi>P</mml:mi><mml:mn>2</mml:mn>

并希望检索2

我的模式是:

/(?:<mml:)(mn|mi|mo)>(.+)(?:<\/mml:\1>)$/

返回值应为2,

但如果字符串是:

<mml:mi>P</mml:mi><mml:mi>s</mml:mi>

然后模式应该从第二组标签内返回s,但是从第一组内部返回P

P</mml:mi><mml:mi>s

按照以下建议更改模式时:

/<mml:(mn|mi|mo)>(.*?)<\/mml:\1>/sU

回报是一样的。 php的行是:

preg_match('/<mml:(mn|mi|mo)>(.*?)<\/mml:\1>/sU', '<mml:mi>P</mml:mi><mml:mi>s</mml:mi>', $ret, PREG_OFFSET_CAPTURE);

和$ ret包含:

Array
(
    [0] => Array
        (
            [0] => <mml:mi>P</mml:mi><mml:mi>s</mml:mi>
            [1] => 0
        )

    [1] => Array
        (
            [0] => mi
            [1] => 5
        )

    [2] => Array
        (
            [0] => P</mml:mi><mml:mi>s
            [1] => 8
        )

)

当更改为已编辑的建议时,用?除去

/<mml:(mn|mi|mo)>(.*)<\/mml:\1>/sU

从第一次出现返回P,而不是从第二次出现的s。

2 个答案:

答案 0 :(得分:1)

从我的手机打字,所以会很简短。

不是匹配任何字符(。+),而是匹配下一个标记开头的任何字符([^&lt;] +)

这样您就不必担心使用反向引用,也不会抓住两个相同标记之间的所有内容。

(仔细检查我把插入符号放在哪里,这是我的头顶。)

要获取最后一次出现,请将整个正则表达式包装在()+

/(<mml:(mn|mi|mo)>([^<]+)<\/mml:\2>)+/

答案 1 :(得分:0)

这是一个优化模式,它不仅比Tim更快地运行,preg_match()将在输出数组中返回更少的元素:

~<m{2}l:(m[ino])>\K[^<](?=</m{2}l:\1>$)~

Pattern Demo

增强:

  1. 将标准模式分隔符斜杠/替换为~,以避免转义以提高简洁性。
  2. 使用连续字符的量词来提高效率。 {2}
  3. 使用字符类而不是管道来提高效率和简洁性。 m[ino]
  4. 使用\K从模式中间开始全字符串匹配,有效地消除了额外捕获组的需要,以提高效率。
  5. 使用否定字符类来匹配所需字符[^<] *注意,如果您想要的子字符串使用多个字符:[^<]+
  6. 使用正向前瞻来准确匹配结束标记,然后是行结束锚$
  7. PHP实施:(Demo

    echo preg_match('~<m{2}l:(m[ino])>\K[^<](?=</m{2}l:\1>$)~','<mml:mi>P</mml:mi><mml:mi>s</mml:mi>',$out)?$out[0]:'fail';
    

    输出:

    s