我正在尝试在preg_match_all
中使用PHP中的reg exp/\d+ (?:<[^>]+>)(?:<[^>]+>)(\S+.*\S+)(?:<[^>]+>)\s*(\S+) (?:L|R)\s*\w* \w*\s*(?:\w+\s*){14}(\d+)\s*(\d)\s*(\d*\xA0*\d{3}\xA0*\d{3})/is
有一些数据样本:
38 <A NAME="Philip McRae"><A HREF="xtrastats.html#Philip McRae">Philip McRae</A> C L OK 58 71 69 49 33 89 71 45 48 69 50 35 32 61 21 3 787 000
43 <A NAME="Alexander Nikulin"><A HREF="xtrastats.html#Alexander Nikulin">Alexander Nikulin</A> C L OK 41 68 71 40 28 90 67 29 31 60 31 37 34 50 26 0 0 000 <a href="http://www.hockeydb.com/ihdb/stats/pdisplay.php?pid=78680" target="_blank">HDB</a>
20 <A NAME="Christian Hanson"><A HREF="xtrastats.html#Christian Hanson">Christian Hanson</A> C R OK 57 72 71 54 33 79 70 42 45 71 46 40 36 60 25 1 875 000 <a href="http://www.hockeydb.com/ihdb/stats/pdisplay.php?pid=73824" target="_blank">HDB</a>
我有大约1500行。
我需要匹配这个:
Philip McRae, C, 21, 3, 787 000 (Name, Position, Age, Contract Lenght, Salary)
每次运行我的代码时,都会出现致命错误:30秒的最大执行时间超出错误。
经过一些搜索后,我在我的脚本顶部添加了这一行,但这并没有解决我的问题
ini_set("pcre.backtrack_limit",10000000);
任何人都可以帮我使用这个reg exp进行一些优化吗?
问候。
帕特里克
答案 0 :(得分:3)
我不会尝试重写你的正则表达式,因为我们没有这些要求,但这里的主要问题是你的名字组:
(\S+.*\S+)
.*
贪婪。这意味着它会消耗尽可能多的东西,包括你期望表达的其余部分匹配的内容,并且它并不止于此。由于您拥有/s
模式修饰符,因此该点也会匹配换行符,从而允许.*
在尝试匹配\S
并开始其长回溯之旅之前使用整个文件。
一种解决方案是使.*
与?
保持懒惰,即.*?
,但由于您知道名称包含在元素中,因此您可以简单地使用否定字符类组:
([^<]*)
这应该可以解决您的问题,但您可能不希望在这种情况下使用/s
模式修饰符,或者至少应该在行模式中添加行锚的起点和终点。您还应该尝试限制使用*
。
答案 1 :(得分:1)
即使你有大约1 500行,你想要解决的问题是每一行。
如果您能够逐行处理输入,那么您已经将问题减少了相当多。
$file = new SplFileObject($path);
foreach ($file as $i => $line) {
printf("#%'0-4d: %s\n", $i, $line);
}
这只是一个例子,当然,正则表达式引擎本身可以使用它的多线修改器(m)做类似的事情。但是,如果您执行上述操作,则可以break
直接进行第一行测试:
foreach ($file as $i => $line) {
printf("#%'0-4d: %s\n", $i, $line);
$pattern = '(^\d++ <A NAME="([^"]++)"><A HREF="xtrastats.html#Philip McRae">Philip McRae</A> C L OK 58 71 69 49 33 89 71 45 48 69 50 35 32 61 21 3 787 000)$';
$r = preg_match($pattern, $line, $matches);
if (FALSE === $r) {
throw new Exception(sprintf("Regex failed (%d)", preg_last_error());
}
if (!$r) {
throw new Exception(sprintf("Pattern does not match."));
}
var_dump($matches);
if ($i > 0) break; # exit foreach after X lines.
}
echo "Done.\n";
正如您在此示例中所看到的那样,模式尚未完成,但您可以逐步替换整行。
它还使用了一个锚点作为字符串开头(^
)和字符串结尾($
)。
它还使用占有量词(+
),这样如果那些不匹配,就不会发生回溯(类似于原子分组,但更容易编写)。
继续逐步改进正则表达式模式。如果正则表达式不编译,则抛出异常。以及当一条线不匹配时。
你应该在一段时间后完成你的工作,从长远来看,改进错误处理并创建一些稳定的高效代码。
答案 2 :(得分:1)
@hakre和@bodhizero
根据您的输入和帮助,我将我的正则表达式修改为:
\d{1,2}+ (?:<[^>]++>)(?:<[^>]++>)([^<]*+)(?:<[^>]++>)\s*+(\S{1,2}+) (?:L|R)\s*+\w*+ \w*+\s*+(?:\w++\s*+){14}(\d{1,2}+)\s*+(\d)\s*(\d*+.*?\d{0,3}+.*?\d{3}+)(?: <[^>]++>[^<]*+<[^>]++>)*?
结果:在大约2秒内解析整个文件!!!
我使用Regexbuddy程序并帮助了我很多。
我希望我能够提出两个答案,但我不能
答案 3 :(得分:0)
您需要限制用于正则表达式匹配的数据量,或更改set_time_limit
和memory_limit
限制。
preg_match_all()
非常占用CPU,根据服务器CPU的强大程度,它可能会导致执行时间和内存问题。
一个解决方案是将其添加到代码顶部:
set_time_limit(0);
ini_set('memory_limit', '128M');
您的另一个选择是将脚本限制为每页加载少preg_match_all()
次匹配。