【问题标题】:Why would this regex return an error?为什么这个正则表达式会返回错误?
【发布时间】:2012-02-02 23:32:58
【问题描述】:

为什么下面的计算结果为true

if(preg_match_all('%<tr.*?>.*?<b>.*?</b>.*?</tr>%ims', $contents, $x)===FALSE)
{...}

$contents,使用file_get_contents()this source 检索。


简化了正则表达式以解决问题。我实际使用的代码是:

if(preg_match(
           '%Areas of Study: </P>.*?<TABLE BORDER="0">(.*?)<TBODY>.*?</TBODY>.*?   </TABLE>%ims',
            $contents, $course_list)
    )
    {
        if(preg_match_all('%<TR>.*?<TD.*?>.*?<B>(.*?)</B>.*?</TD>.*?<TD.*?>.*?</TD>.*?<TD.*?>.*?<B>(.*?)</B>.*?</TD>.*?</TR>%ims',
                $course_list[0], $course_titles)
        )
        {
            ...
        }
        else 
        {
            die('<p>ERROR: first preg_match_all fails</p>');
        }

        echo '<p>INFO: Courses  found</p>';
    }
    else
    {
        die('<p>ERROR: Courses not found</p>');
    }

    if(
        preg_match_all('%<tr.*?>.*?<b>.*?first '.$college.' area of study.*?</b>.*?</tr>.*?<tr.*?>.*?<td.*?>.*?<b>(.*?) \((.*?)\).*?</b>(.*?credits.*?)</td>.*?<td.*?>(.*?<a .*?)</td>.*?</tr>%ims',
        $contents, $course_modules))
    {
        ....
    }
    else
    {
        die('<p>ERROR: Courses details/streams not found</p>');
    }

我总是得到:

信息:找到的课程
错误:找不到课程详细信息/流

奇怪的是,其他正则表达式函数调用似乎如何工作,但不是最后一个。


注意:

这个正则表达式以前有效(实际上更复杂)。我不确定这是否重要,但是我更新了我的 WAMP 版本(因此我的 php.ini 等被重置)并且我在上周对 MongoDB 连接问题进行故障排除时弄乱了我的设置.

【问题讨论】:

  • 因为你在HTML上使用它stackoverflow.com/questions/1732348/…即使你这次找到了一个聪明的解决方案,明天你更新你的___时它会失败。 FWIW,这可能与区分大小写有关吗?
  • @Amarghosh 我学习了一个关于使用正则表达式解析 HTML 的整个模块:哦,我们的 CS 部门负责人也教过这个模块!源不是 xHTML 是否重要?来源:ucc.ie/calendar/science/sci002.html
  • @Amarghosh 我在分隔符之后的末尾有i...这使得它不区分大小写
  • 看起来应该对我有用。只是在黑暗中拍摄,但也许可以尝试更改分隔符(# 而不是 %)。
  • @ChristopherJohnson 谢谢,但不行

标签: php regex html-parsing wamp preg-match-all


【解决方案1】:

我添加第二个答案是为了响应您在发布第一个答案后添加的新信息。我的目标是在正则表达式工作时帮助您将系统恢复到之前的状态。我倾向于同意我链接到的那个页面上的评论者,他说默认设置过于保守。所以我支持这个答案,但我不希望任何人认为他们可以通过向他们投入更多内存来解决所有正则表达式问题。

既然我已经看到了您在现实世界中的正则表达式,我不得不说您还有另一个问题。我针对您在 RegexBuddy 中链接到的页面测试了第三个正则表达式,这些是我得到的结果:

(?ims)<tr.*?>.*?<b>.*?first science area of study.*?</b>.*?</tr>.*?<tr.*?>.*?<td.*?>.*?<b>(.*?) \((.*?)\).*?</b>(.*?credits.*?)</td>.*?<td.*?>(.*?<a .*?)</td>.*?</tr>

          course name       start      end    steps
Match #1  (Comp. Sci.)        10       275    31271
Match #2  (Bio & Chem)       276       341     6986
Match #3  (Enviro)           342       379     5944
Match #4  (Genetics)         386       416     4463
Match #5  (Chem)             417       455     5074
Match #6  (Math)             495       546    15610
Match #7  (Phys & Astro)     547       593     8617
Match #8  (no match)        gave up after 1,000,000 steps

你可能听过很多人说非贪婪的正则表达式总是返回最短的匹配,那么为什么这个返回的第一个匹配比其他任何一个都长 200 行?你可能听说过它们更高效,因为它们不会回溯那么多,那么为什么这个要花费超过 30,000 步才能完成第一场比赛,以及为什么在没有比赛可能的情况下有效锁定最后一次尝试?

首先,不存在贪婪或非贪婪的正则表达式。只有个别的量词可以这样描述。每个量词都是贪婪的正则表达式不一定会返回最长的匹配,并且名称“非贪婪的正则表达式”甚至更不准确。无论是贪心还是非贪心,正则表达式引擎总是在最早的机会开始尝试匹配,并且在探索完所有可能的路径之前,它不会放弃起始位置。

非贪心量词只是为了方便;他们没有什么神奇之处。引导正则表达式引擎进行正确有效的匹配仍然取决于您,正则表达式作者。您的正则表达式可能会返回正确的结果,但在此过程中浪费了大量精力。它消耗了很多一开始就不需要的字符,它一遍又一遍地检查相同的字符,而且它需要很长时间才能弄清楚它所在的路径何时不能导致匹配.

现在看看这个正则表达式:

(?is)<tr[^<]*(?:<(?!/tr>|b>)[^<]*)*<b>\s*first science area of study\s*</b>.*?</tr>.*?<tr.*?>.*?<td.*?>.*?<b>(.*?) \((.*?)\).*?</b>(.*?credits.*?)</td>.*?<td.*?>(.*?<a .*?)</td>.*?</tr>

          course name       start      end    steps
Match #1  (Comp. Sci.)       209       275     9891
Match #2  (Bio & Chem)       276       341     5389
Match #3  (Enviro)           342       379     5833
Match #4  (Genetics)         386       416     4222
Match #5  (Chem)             417       455     4961
Match #6  (Math)             495       546     9899
Match #7  (Phys & Astro)     547       593     8506
Match #8  (no match)        reported failure in 139 steps

在第一个&lt;/b&gt; 之后,一切都如您所写。我的更改的效果是,它直到找到包含我们感兴趣的第一个 &lt;B&gt; 标记的 &lt;TR&gt; 元素后才开始真正匹配:

<tr[^<]*(?:<(?!/tr>|b>)[^<]*)*<b>\s*first science area of study\s*</b>

这部分大部分时间都在用[^&lt;]* 贪婪地消耗字符,这比非贪婪的.*? 要快得多。但更重要的是,几乎不用花时间就可以确定何时无法再进行匹配。如果有正则表达式性能的黄金法则,那就是:当匹配尝试失败时,它应该尽快失败。

【讨论】:

    【解决方案2】:

    您可以检查您的pcre.backtrack_limit 设置。阻止该正则表达式匹配该输入必须非常低,但您确实说过您一直在搞乱设置...

    您可以尝试通过更改正则表达式来测试它。当我在 RegexBuddy 中对其进行测试时,您的正则表达式在 1216 步中匹配了该输入。当我把它改成这样:

    '%<tr.*?>.*?<b>.*?</b>[^<]*(?:<(?!/?tr\b)[^<]*)*</tr>%ims'
    

    ...只用了 441 步。

    【讨论】:

    • 那个正则表达式也返回了false。我在我的 php.ini 中找到了 ;pcre.backtrack_limit=100000 并将其更改为 pcre.backtrack_limit=100000 但它也没有任何区别
    • 好的,结果是取消注释backtrack_limit 行并添加三个零对其进行排序;即pcre.backtrack_limit=100000000
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多