2015-01-09 1 views
3

Я столкнулся с нечетной проблемой. оказывается я достичь какой-то предел с preg_replace при попытке использовать два матча с использованием PHP-5.3.3preg_match, похоже, достиг предела при использовании двух совпадений

// works fine 
$pattern_1 = '?START(.*)STOP?'; 
$string = 'START' . str_repeat('x',9999999) . 'STOP' ; 
preg_match($pattern_1, $string , $matchedArray)  ; 

$pattern_2 = '?START-ONE(.*)STOP-ONE.*START-TWO(.*)STOP-TWO.*?'; 

// works fine 
$string = 'START-ONE this is head stuff STOP-ONE START-TWO' . str_repeat('x', 49970) . 'STOP-TWO' ; 
preg_match($pattern_2, $string , $matchedArray_2)  ; 

// didnt work 
$string = 'START-ONE this is head stuff STOP-ONE START-TWO' . str_repeat('x', 49971) . 'STOP-TWO' ; 
preg_match($pattern_2, $string , $matchedArray_3)  ; 

Первый вариант только один матч использует очень большой строки и не имеет никаких проблем.

Второй вариант имеет длину строки 50 026 и отлично работает. последний вариант имеет длину строки 50 027 (еще один), и совпадение больше не работает. поскольку число 49971 может меняться при возникновении ошибки, оно может быть изменено на нечто большее, чтобы имитировать проблему.

Любые идеи или мысли? возможно, это проблема с версией php? возможно, обходным путем является просто использовать только одно совпадение, а не два, а затем запустить preg_match в два раза?

+0

Вы достигли предела памяти; попробуйте увеличить его в своем 'PHP.ini' –

ответ

2

Хорошо, PHP не очень разговорчив о ошибках регулярных выражений, он просто возвращает false для последнего случая, который просто говорит, чем произошла ошибка, за PHP docs.

Я воспроизвел проблему, используя PCRE (двигатель регулярных выражений, используемый preg_match) в C# (но с гораздо большим количеством символов), а ошибка, которую я получаю, - PCRE_ERROR_MATCHLIMIT.

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

Чтобы устранить эту проблему, вы можете установить более высокое значение параметра pcre.backtrack_limit PHP, который контролирует этот предел:

ini_set("pcre.backtrack_limit", "10000000"); // Actually, this is PCRE's default 

На стороне записки:

  • Вы, вероятно, следует заменить (.*) с (.*?) чтобы получить меньше бесполезного возврата и для правильности (иначе двигатель регулярного выражения пройдет мимо строки STOP и должен будет вернуться к нему)
  • Использование ? в качестве разделителя шаблона является идеей bad, так как это предотвращает использование метасимвола ? и, следовательно, применение вышеуказанного совета. Действительно, вы должны никогда использовать метасимволы регулярных выражений как разделители шаблонов.

Если вы заинтересованы в деталях более низкого уровня, вот соответствующий бит PCRE DOCS (курсив мой):

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

Внутренне, pcre_exec() использует функцию, называемую match(), которую он вызывает многократно (иногда рекурсивно).Предел, установленный на match_limit, накладывается на количество вызов этой функции во время совпадения, , что приводит к ограничению суммы обратного отслеживания, которое может иметь место. Для шаблонов, которые не привязаны, число отсчитывается от нуля для каждой позиции в строке темы.

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

Значение по умолчанию для предела может быть установлено при построении PCRE; по умолчанию по умолчанию - 10 миллионов, который обрабатывает все, кроме самых экстремальных случаев. Вы можете переопределить значение по умолчанию, добавив pcre_exec() с блоком pcre_extra, в котором установлено match_limit, а PCRE_EXTRA_MATCH_LIMIT установлено в поле flags. Если предел превышен, pcre_exec() возвращает PCRE_ERROR_MATCHLIMIT.

Значение предела для соответствия также может быть поставлено элементом в начале шаблона формы

(*LIMIT_MATCH=d) 

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

+0

. Ваша мысль об исключении метасимволов ** отлично **, спасибо за указание на это. я на самом деле читаю на веб-страницах и пытаюсь разобрать секцию тела, следовательно, необходимо скобки (но я уверен, вы скажете мне, что php имеет функцию только для этого). всегда кажется странным изменить php.ini только для одной программы. теперь я просто использую две preg_matches, но в ближайшее время попробую ваше предложение. еще раз спасибо. – edwardsmarkf

+0

Добро пожаловать. И да, вы должны, вероятно, использовать [лучшие инструменты] (http://stackoverflow.com/a/3577662/3764814), поскольку разбор HTML с регулярным выражением [не для всех] (http://stackoverflow.com/a/1732454/3764814) :) –

+0

Кроме того, 'ini_set' изменяет значение только для текущего запроса, оно не является постоянным (оно не меняет php.ini). Таким образом, вы можете пойти и использовать его. –