По сути, «. *» (Соответствует любому количеству чего-либо) означает попытку сопоставить всю строку, если она не совпадает, затем вернуться и повторить попытку и т. Д. Использование одного из них не слишком много проблема, но время, необходимое для использования более одного, увеличивается в геометрической прогрессии. Это довольно глубокое (и гораздо более точное) обсуждение такого рода вещей: http://discovery.bmc.com/confluence/display/Configipedia/Writing+Efficient+Regex
РЕДАКТИРОВАТЬ: (я надеюсь, вы действительно хотели знать, почему)
Пример строки источника:
aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks
ОДИН СПОСОБ СМОТРЕТЬ НА ЭТО:
Процесс занимает так много времени, потому что .*
соответствует всей исходной строке (aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks
), только чтобы обнаружить, что он не заканчивается символом |
, а затем возвращается к последнему случаю |
символ (...4876|2345...
), затем пытается найти следующий .*
до конца строки.
Он начинает поиск следующего |
символа, указанного в вашем выражении, и не находит его, затем возвращается к первому сопоставленному символу |
(указанному в ...4876|2345...
), отбрасывает это совпадение и находит ближайший |
перед ним (...dfas|2936...
), чтобы он мог соответствовать второму символу |
в вашем выражении соответствия.
Затем он будет сопоставлять .*
с 2936827634876
, а второй |
- с одним из ...4876|2345...
, а следующий .*
- с оставшимся текстом, только чтобы найти, что вы хотели еще один |
. Затем он будет продолжать возвращаться снова и снова, пока не совпадет со всеми символами, которые вы указали.
ДРУГОЙ СПОСОБ СМОТРЕТЬ НА ЭТО:
(Исходное выражение):
.*\Q|\E.*\Q|\E.*\Q|\E.*bundle.*
это примерно переводится на
match:
any number of anything,
followed by a single '|',
followed by any number of anything,
followed by a single '|',
followed by any number of anything,
followed by a single '|',
followed by any number of anything,
followed by the literal string 'bundle',
followed by any number of anything
проблема в том, что any number of anything
включает |
символов, требующих многократного разбора всей строки, где вы на самом деле имеете в виду any number of anything that is not a '|'
Чтобы исправить или улучшить выражение, я бы порекомендовал три вещи:
Сначала (и наиболее значимый) , замените большинство "совпадений с чем угодно" (.*
) классами отрицанных символов ([^|]
), например так:
[^|]*\Q|\E[^|]*\Q|\E[^|]*\Q|\E.*bundle.*
... это будет препятствовать тому, чтобы оно совпадало с концом строки снова и снова, но вместо этого сопоставляло бы все не |
символы до первого символа, который не является "не |
символом "(это двойное отрицание означает до первого |
символа), затем соответствует символу |
, затем идет к следующему и т. д ...
Второе изменение (несколько значимое, в зависимости от исходной строки) должно приводить к тому, что второе-последнее "соответствует любому количеству чего-либо" (.*
) в "ленивый" или " неохотно "типа" любое число "(.*?
). Это заставит его пытаться сопоставить что-либо с идеей поиска bundle
вместо пропуска bundle
и сопоставления остальной части строки, только чтобы понять, что есть еще что-то, чтобы соответствовать, как только он туда добирается, вынужденный откатываться назад , Это приведет к:
[^|]*\Q|\E[^|]*\Q|\E[^|]*\Q|\E.*?bundle.*
Третье изменение Я бы порекомендовал для удобочитаемости - замените блоки \Q\E
одиночным экранированием, как в \|
, например:
[^|]*\|[^|]*\|[^|]*\|[^|].*?bundle.*
Это - то, как выражение всегда обрабатывается внутренне - есть буквально функция, которая преобразует выражение, чтобы "экранировать все специальные символы между \ Q и \ E" - \Q\E
только сокращение, и если это делает не делайте ваше выражение короче или проще для чтения, его не следует использовать. Период.
Классы отрицанных символов имеют неотключенный |
, потому что |
не является специальным символом в контексте классов символов - но давайте не будем слишком отвлекаться. Вы можете избежать их, если хотите, но не обязаны.
Окончательное выражение примерно переводится на:
match:
any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything, up until the next expression can be matched,
followed by the literal string 'bundle',
followed by any number of anything
Хороший инструмент, который я использую (но стоит немного денег), называется RegexBuddy - бесплатный / бесплатный веб-сайт для понимания регулярных выражений http://www.regular -expressions.info , а конкретная страница, объясняющая повторение, - http://www.regular -expressions.info / repeat.html
RegexBuddy эмулирует другие движки регулярных выражений и говорит, что вашему исходному регулярному выражению потребовалось бы 544 «шага» для сравнения, а не 35 «шагов» для предоставленной мной версии.
SLIGHTLY LONGER Пример исходной строки A:
aaffg, ";;p[p09978|ksjfsadfas|12936827634876|2345.4564a bundle of sticks
SLIGHTLY LONGER Пример исходной строки B:
aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks4me
Более длинная исходная строка 'A' (добавлено 1
до 2936827634876
) не повлияло на предложенную мной замену, но увеличило оригинална 6 шагов
Более длинная исходная строка 'B' (добавленная '4me' в конце выражения) снова не повлияла на предложенную мной замену, но добавила 48 шагов к исходному
Таким образом, в зависимости от того, чем строка отличается от приведенных выше примеров, строка из 60 КБ может выполнить всего 544 шага или более миллиона шагов