PHP REG EXP проблема возврата

Я пытаюсь использовать этот reg exp в PHP в preg_match_all

/\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 для некоторой оптимизации?

С уважением.

Патрик

4 ответа

Решение

Я не буду пытаться переписать ваше регулярное выражение, поскольку у нас нет требований, но главная проблема здесь - ваша группа имен:

(\S+.*\S+)

.* жадный Это означает, что он будет израсходовать столько, сколько может, включая то, что вы ожидаете, что остальная часть вашего выражения будет соответствовать, и это не остановится на этом. Так как у вас есть /s Модификатор шаблона, точка также будет соответствовать символам новой строки, позволяя .* потреблять весь файл, прежде чем пытаться сопоставить \S и начинает свое долгое путешествие в обратном направлении.

Одним из решений является сделать .* ленивый с ?т.е. .*? но поскольку вы знаете, что имя содержится в элементе, вы можете просто использовать отрицательный символьный класс для всей группы:

([^<]*)

Это должно исправить вашу проблему, но вы, вероятно, не хотите использовать /s модификатор шаблона в этом случае, или вы должны по крайней мере добавить начало и конец якоря линии к вашему шаблону. Вы также должны попытаться ограничить использование *,

Пожалуйста, смотрите: Катастрофическое возвращение назад и следите за жадностью

Даже если у вас есть около 1 500 строк, проблема, которую вы хотели бы решить, относится к каждой строке.

Если вы можете обрабатывать ввод построчно, вы уже уменьшили проблему на значительную сумму.

$file = new SplFileObject($path);
foreach ($file as $i => $line) {
    printf("#%'0-4d: %s\n", $i, $line);
}

Это всего лишь пример, естественно, сам механизм регулярных выражений может сделать нечто подобное с его многострочным модификатором (m). Однако, если вы сделаете foreach выше, вы могли бы 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";

Как вы можете видеть в этом примере, шаблон еще не завершен, но вы работаете от полной строки, заменяя шаг за шагом.

Он также использует якорь для начала строки (^) и для конца строки ($).

Он также использует собственнические квантификаторы (+) так что если они не совпадают, обратного отслеживания не происходит (аналогично атомарной группировке, но легче написать).

Продолжайте шаг за шагом улучшать шаблон регулярных выражений. Если регулярное выражение не компилируется, генерируется исключение. Как и когда строка не совпадает.

Вы должны сделать свое дело через некоторое время, улучшить обработку ошибок и создать стабильный эффективный код в долгосрочной перспективе.

@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 и очень мне помогаю.

Хотел бы я поставить два ответа, но не могу

Вам необходимо ограничить объем данных, используемых для сопоставления регулярных выражений, или изменить set_time_limit а также memory_limit пределы.

preg_match_all() очень сильно загружает процессор, и в зависимости от того, насколько мощен ваш серверный процессор, это может вызвать время выполнения и проблемы с памятью.

Одно из решений - добавить это в начало вашего кода:

set_time_limit(0);
ini_set('memory_limit', '128M');

Ваш другой вариант ограничивает сценарий меньшим preg_match_all() совпадений на странице загрузки.

Другие вопросы по тегам