PHPpreg_match_all麻烦
PHP preg_match_all trouble
我写了一个正则表达式,我在 rubular.com 中测试过,它返回了 4 个匹配项。可以在此处 http://pastebin.com/49ERrzJN 找到测试主题,下面是 PHP 代码。出于某种原因,PHP 代码 returns 仅匹配前 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>';
你的正则表达式太慢了。在 regex101.com 上尝试后,我发现它会在 PHP 上超时(但不是 JS,无论出于何种原因)。我很确定超时发生在 50,000 步左右。实际上,现在您不使用在线 PHP 正则表达式测试器的原因是有道理的。
我不确定这是否是您问题的根源,但是 there is a default memory limit in PHP:
memory_limit [default:] "128M"
[history:] "8M" before PHP 5.2.0, "16M" in PHP 5.2.0
如果你使用 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个。它们在开头,锚 ^
和文字 space.
如果你必须写一个长模式,首先要做的是让它可读。为此,请使用允许注释和自由间距的详细模式(x 修饰符),并使用命名捕获。
那么你需要准确描述你要找的东西:
- your target takes a whole line => 使用带有修饰符 m 的锚点
^
和 $
,并使用 \h
class (仅包含水平空格) 而不是 \s
class.
- 与其使用这种低效的子模式
(?:(?!.....).){m,n}
来描述您的字段不能包含的内容,不如描述字段可以包含的内容。
- 在需要时使用原子组
(?>...)
而不是非捕获组以避免无用的回溯。
- 一般来说,使用精确的字符classes可以避免很多问题
模式:
~
^ \h*+ # start of the line
# named captures # field separators
(?<VOTERNO> [0-9]+ ) \h+
(?<SURNAME> \S+ (?>\h\S+)*? ) \h{2,}
(?<OTHERNAMES> \S+ (?>\h\S+)*? ) \h{2,}
(?<DOB> [0-9]{2}-[0-9]{2}-[0-9]{4} ) \h+
(?<SEX> [FM] ) \h+
(?<APPID_RECNO> [0-9A-Z/]+ ) \h+
(?<VILLAGE> \S+ (?>\h\S+)* )
\h* $ # end of the line
~mx
如果你想知道一个模式出了什么问题,你可以使用函数preg_last_error()
我写了一个正则表达式,我在 rubular.com 中测试过,它返回了 4 个匹配项。可以在此处 http://pastebin.com/49ERrzJN 找到测试主题,下面是 PHP 代码。出于某种原因,PHP 代码 returns 仅匹配前 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>';
你的正则表达式太慢了。在 regex101.com 上尝试后,我发现它会在 PHP 上超时(但不是 JS,无论出于何种原因)。我很确定超时发生在 50,000 步左右。实际上,现在您不使用在线 PHP 正则表达式测试器的原因是有道理的。
我不确定这是否是您问题的根源,但是 there is a default memory limit in PHP:
memory_limit [default:] "128M"
[history:] "8M" before PHP 5.2.0, "16M" in PHP 5.2.0
如果你使用 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个。它们在开头,锚 ^
和文字 space.
如果你必须写一个长模式,首先要做的是让它可读。为此,请使用允许注释和自由间距的详细模式(x 修饰符),并使用命名捕获。
那么你需要准确描述你要找的东西:
- your target takes a whole line => 使用带有修饰符 m 的锚点
^
和$
,并使用\h
class (仅包含水平空格) 而不是\s
class. - 与其使用这种低效的子模式
(?:(?!.....).){m,n}
来描述您的字段不能包含的内容,不如描述字段可以包含的内容。 - 在需要时使用原子组
(?>...)
而不是非捕获组以避免无用的回溯。 - 一般来说,使用精确的字符classes可以避免很多问题
模式:
~
^ \h*+ # start of the line
# named captures # field separators
(?<VOTERNO> [0-9]+ ) \h+
(?<SURNAME> \S+ (?>\h\S+)*? ) \h{2,}
(?<OTHERNAMES> \S+ (?>\h\S+)*? ) \h{2,}
(?<DOB> [0-9]{2}-[0-9]{2}-[0-9]{4} ) \h+
(?<SEX> [FM] ) \h+
(?<APPID_RECNO> [0-9A-Z/]+ ) \h+
(?<VILLAGE> \S+ (?>\h\S+)* )
\h* $ # end of the line
~mx
如果你想知道一个模式出了什么问题,你可以使用函数preg_last_error()