Когда в учебниках вводятся обходные пути, они, как правило, выбирают самый простой вариант использования для каждого из них. Таким образом, они будут использовать примеры, такие как (?<!a)b
(«b» не предшествует «a») или q(?=u)
(«q», за которым следует «u»). Это просто для того, чтобы не загромождать объяснение отвлекающими деталями, но оно имеет тенденцию создавать (или усиливать) впечатление, что взгляды за спиной и впереди должны появляться в определенном порядке. Мне потребовалось довольно много времени, чтобы избавиться от этой идеи, и я видел, как несколько других тоже страдали от нее.
Попробуйте взглянуть на несколько более реалистичных примеров. Один вопрос, который часто возникает, касается проверки паролей; например, убедитесь, что новый пароль имеет длину не менее шести символов и содержит как минимум одну букву и одну цифру. Один из способов сделать это будет:
^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$
Класс символов [A-Za-z0-9]{6,}
может совпадать со всеми буквами или всеми цифрами, поэтому вы используете предпросмотры, чтобы убедиться, что есть хотя бы одна из них. В этом случае вы должны выполнить предварительные запросы first , потому что более поздние части регулярного выражения должны быть в состоянии исследовать всю строку.
В качестве другого примера, предположим, что вам нужно найти все вхождения слова "там", если ему не предшествует кавычка. Очевидное регулярное выражение для этого - (?<!")[Tt]here\b
, но если вы ищете большой корпус, это может создать проблему производительности. Как написано, это регулярное выражение будет негативно смотреть на каждую позицию в тексте, и только когда это удастся, оно будет проверять оставшуюся часть регулярного выражения.
Каждый движок регулярных выражений имеет свои сильные и слабые стороны, но одна вещь, которая верна для всех из них, заключается в том, что они быстрее находят фиксированные последовательности буквенных символов, чем что-либо еще - чем длиннее последовательность, тем лучше. Это означает, что может быть значительно быстрее выполнить просмотр за последним , даже если это означает совпадение слова дважды:
[Tt]here\b(?<!"[Tt]here)
Таким образом, правило, регулирующее размещение lookarounds, состоит в том, что правила не существует; Вы помещаете их туда, где они имеют смысл в каждом конкретном случае.