Вам нужно регулярное выражение, которое сопоставляет литералы регулярных выражений в файлах исходного кода, чтобы вы могли легко найти их в текстовом редакторе или с помощью инструмента grep. В вашем языке программирования для разделения регулярных выражений используются прямые косые черты. Прямые косые черты в регулярном выражении должны быть экранированы обратным слешем.
Ваш регекс должен соответствовать только тому, что выглядит как литерал регулярного выражения. Ему не нужно проверять, что текст между парой прямых косых черт действительно является правильным регулярным выражением.
Поскольку вы будете использовать только один regex, а не писать полноценный компилятор, ваше регулярное выражение должно быть достаточно умным, чтобы понять разницу между прямой косой чертой, используемой в качестве оператора деления, и косой чертой, используемой для начала regex. В исходном коде литеральные регулярные выражения появляются как часть присваиваний (после знака равенства), в тестах на равенство или неравенство (после знака равенства), возможно, с оператором отрицания (восклицательным знаком) перед регексом, в литеральных определениях объектов (после двоеточия) и в качестве параметра функции (после открывающей скобки или запятой). Пробел между регексом и символом, который ему предшествует, должен игнорироваться.
Решение
1 | (?<=[=:(,](?:\s*!)?\s*)/[^/\\\r\n]*(?:\\.[^/\\\r\n]*)*/ |
1 | [=:(,](?:\s*!)?\s*\K/[^/\\\r\n]*(?:\\.[^/\\\r\n]*)*/ |
1 | (?<=[=:(,](?:\s{0,10}+!)?\s{0,10})/[^/\\\r\n]*(?:\\.[^/\\\r\n]*)*/ |
1 | [=:(,](?:\s*!)?+\s*(/[^/\\\r\n]*(?:\\.[^/\\\r\n]*)*/) |
Обсуждение
Все четыре решения используют '/[^/\\\\r\n]*(?:\\\.[^/\\\\r\n]*)*/' для соответствия регулярному экс-выражению. Это то же самое регулярное выражение, что и в решении рецепта 7.9, только, что вместо кавычек в нем стоят прямые косые черты. Буквальное регулярное выражение - это просто строка, заключенная в кавычки с прямыми косыми чертами, которая может содержать прямые косые черты, если их экранировать с помощью обратной косой чертой.
Разница между этими четырьмя решениями заключается в том, как они проверяют, является ли предзнаком равенства, двоеточием, открывающей скобкой или запятой, возможно, с восклицательным знаком между этим символом и регулярным выражением. Мы могли бы легко сделать это с помощью lookbehind, если бы мы также не хотели разрешить любое количество пробельных символов между регулярным выражением и предшествующим символом. Это усложняет дело, поскольку рассматриваемые в этой книге варианты regex сильно различаются по поддержке lookbehind.
Для .NET у нас есть идеальное решение: '(?<=[=:(,](?:\s*!)?\s*)'. Символ класс символов '[=:(,]' проверяет наличие любого из четырех символов. '(?:\s*!)?» позволяет символу следовать за восклицательным знаком с любым количеством пробела между символом и восклицательным знаком. Второе '\s*' позволяет запрещает любое количество пробельных символов перед прямой косой чертой, которая открывает регекс.
Perl и PCRE не допускают повторений внутри lookbehind. Решение, использующее lookbehind не будет достаточно гибким в Perl или PCRE. Но Perl 5.10 и PCRE 7.2 добавили новый
regex-токен '\K', который мы можем использовать вместо него. Мы используем '[=:(,](?:\s*!)?\s*' для соответствия любому из четырех символов, за которыми по желанию следует любое количество пробельных символов и восклицательная и восклицательной точкой, а также после любого пробельного символа. После того как регекс сопоставил эти символы, '\K' указывает регексу сохранить то, что он только что сопоставил.
Символы пунктуации, только что сопоставленные нашим регексом, не будут включены в общийрезультат совпадения. Процесс совпадения будет продолжаться нормально с '/[^/\\\\r\n]*(?:\\\.[^/\\\\\r\n]*)*/' для соответствия регулярному выражению.
Java не допускает бесконечного повторения в lookbehind, но допускает конечное повторение. Поэтому вместо того, чтобы использовать '\s*' для проверки абсолютно любого количества пробельных символов, мы используем '\s{0,10}' для проверки на наличие до 10 пробельных символов. Число 10 произвольно; нам просто нужно что-то достаточно большое, чтобы не пропустить регексы, которые глубокие отступы. Нам также нужно, чтобы это число было достаточно небольшим, чтобы убедиться, что мы чтобы не замедлять работу регулярного выражения. Чем больше число повторений повторений, тем больше символов просканирует Java в поисках совпадения с тем, что находится внутри lookbehind.
Другие варианты regex либо не поддерживают повторения внутри lookbehind, либо не поддерживают переносить lookbehind или '\K' вообще. Для этих вариантов мы просто используем '[=:(,](?:\s*!)?+\s*' для соответствия пунктуации перед регексом, и '(/[^/\\\\r\n]*(?:\\\.[^/\\\\r\n]*)*/)' для соответствия самому регексу и сохранения его в группе захвата.
Общий регекс будет включать как пунктуацию, так и регекс. Группа захвата позволяет проще получить только регекс. Это решение будет работать только в том случае, если приложение в котором вы будете использовать этот регекс, может работать с текстом, сопоставленным с группой захвата, а не с, а не со всем регексом.