为什么这个正则表达式会返回错误?

时间:2012-02-02 23:32:59

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

为什么以下评估为true

if(preg_match_all('%<tr.*?>.*?<b>.*?</b>.*?</tr>%ims', $contents, $x)===FALSE)
{...}
使用this source中的$contents检索

file_get_contents()


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

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连接问题时搞乱了我的设置。

2 个答案:

答案 0 :(得分:3)

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

既然我已经看过你的真实世界正义,我不得不说你还有另一个问题。我在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

在第一个</b>之后,一切都像你写的一样。我的更改的效果是,在找到包含我们感兴趣的第一个<TR>标记的<B>元素之前,它不会真正开始匹配:

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

这一部分大部分时间花在使用[^<]*的贪婪消费字符上,这比非贪婪的.*?快得多。但更重要的是,几乎没有时间弄清楚什么时候不可能有更多的匹配。如果有正则表达式的黄金法则就是这样:当匹配尝试失败时,它应该尽快失败。

答案 1 :(得分:1)

您可以查看pcre.backtrack_limit设置。为了防止正则表达式匹配那个输入,它必须是非常低的,但你确实说你一直在搞乱设置......

您可以尝试通过更改正则表达式来测试它。当我在RegexBuddy中测试它时,你的正则表达式以1216步为单位匹配该输入。当我把它更改为:

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

......它只花了441步。