PHP preg_match_all麻烦

时间:2016-04-03 11:14:03

标签: regex regex-greedy

我写了一个正则表达式,我在rubular.com中测试过,它返回了4个匹配项。测试主题可以在http://pastebin.com/49ERrzJN找到,PHP代码如下。由于某种原因,PHP代码仅返回前2个匹配项。如何使它匹配所有4?它似乎与贪婪有关。

$file = file_get_contents('x.txt');
preg_match_all('~[0-9]+\s+(((?!\d{7,}).){2,20})\s{2,30}(((?!\d{7,}).){2,30})\s+([0-9]+)-([0-9]+)-([0-9]+)\s+(F|M)\s+(.{3,25})\s+(((?!\d{7,}).){2,50})~', $file, $m, PREG_SET_ORDER);
foreach($m as $v) echo 'S: '. $v[1]. '; N: '. $v[3]. '; D:'. $v[7]. '<br>';

2 个答案:

答案 0 :(得分:2)

你的正则表达式非常懒散。在regex101.com上试用之后,我发现它会在PHP上超时(但不是JS,无论出于何种原因)。我很确定超时发生在大约50,000步。实际上,现在为什么你没有使用在线PHP正则表达式测试器是有道理的。

我不确定这是否是您问题的根源,但there is a default memory limit in PHP:

  

memory_limit [默认:]“128M”

     

[history:] PHP 8.0之前的“8M”,PHP 5.2.0中的“16M”

如果您使用m ultiline修饰符(我假设preg_match_all实际上添加了g lobal修饰符),您可以使用此正则表达式只需1282步即可找到所有4个匹配项:

^ [0-9]+\s+(((?!\d{7,}).){2,20})\s{2,30}(((?!\d{7,}).){2,30})\s+([0-9]+)-([0-9]+)-([0-9]+)\s+(F|M)\s+(.{3,25})\s+(((?!\d{7,}).){2,50})

实际上,我添加了只有2个字符。他们在开头,锚^和文字空间。

答案 1 :(得分:1)

如果你必须写一个长模式,首先要做的是让它可读。为此,请使用允许注释和自由间距的详细模式(x修饰符),并使用命名捕获。

然后你需要准确描述你在寻找什么:

  • 您的目标占据整行=&gt;将锚点^$与修饰符m一起使用,并使用\h(仅包含水平空格)而不是{{1 }。class。
  • 而不是使用这种低效的子模式\s来描述您的字段不得包含的内容,描述该字段可以包含的内容。
  • 在需要时使用原子组(?:(?!.....).){m,n}而不是非捕获组,以避免无用的回溯。
  • 一般来说,使用精确的字符类可以避免很多问题

图案:

(?>...)

demo

如果您想知道模式出了什么问题,可以使用函数preg_last_error()