Нежадное регулярное выражение dotall в Python

Мне нужно разобрать аннотации методов, написанных на PHP. Я написал регулярное выражение (см. упрощенный пример ниже) для их поиска, но оно не работает должным образом. Вместо сопоставления самой короткой части текста между /** и */ он соответствует максимальному объему исходного кода (предыдущие методы с аннотациями). Я уверен, что использую правильную .*? не жадную версию *, и я не нашел доказательств того, что DOTALL отключает ее. Подскажите, где может быть проблема? Спасибо.

p = re.compile(r'(?:/\*\*.*?\*/)\n\s*public', re.DOTALL)
methods = p.findall(text)

person user3853423    schedule 31.07.2014    source источник
comment
У меня работает нормально. Удаление re.DOTALL заставляет его снова работать?   -  person Matthew    schedule 31.07.2014
comment
Не могли бы вы включить несколько примеров данных для text в строку в тройных кавычках, чтобы мы могли скопировать и вставить пример в сеанс интерпретатора и сами видите проблему? (Убедитесь, что в примере данных отображается проблема, если вы копируете и вставляете пример в сеанс интерпретатора.)   -  person user2357112 supports Monica    schedule 31.07.2014
comment
лучше бы пример.   -  person Avinash Raj    schedule 31.07.2014
comment
Нет, без re.DOTALL вообще не получится - ничего не нашел.   -  person user3853423    schedule 31.07.2014
comment
\s также соответствует символу \n.   -  person Avinash Raj    schedule 31.07.2014
comment
@ user3853423: Вставьте это в код вопроса.   -  person user2357112 supports Monica    schedule 31.07.2014
comment
похоже, что ваш второй /* * содержит пробел.   -  person Avinash Raj    schedule 31.07.2014
comment
Я предполагаю .*? тоже может соответствовать пространству, я прав?   -  person user3853423    schedule 31.07.2014


Ответы (3)


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

Если вы хотите исключить */ из комментария, вам нужно сгруппировать . с предварительным утверждением:

(?:(?!\*/).)

(?!\*/) утверждает, что символ, который мы сопоставляем, не является началом последовательности */.

person user2357112 supports Monica    schedule 31.07.2014
comment
Это должно выглядеть так? p = re.compile(r'(?:/\*\*(?!\*/).*?\*/)\n\s*public', re.DOTALL) - person user3853423; 31.07.2014
comment
@ user3853423: Да, я только что понял, что забыл упомянуть, что утверждение должно быть сгруппировано с ., поэтому *? каждый раз запускает утверждение. Это должно выглядеть как r'(?:/\*\*(?:(?!\*/).)*?\*/)\n\s*public'. Между прочим, это регулярное выражение достаточно запутано, поэтому использование подробного режима наверное хорошая идея. - person user2357112 supports Monica; 31.07.2014
comment
Сейчас я использую r'(?:/\*\*(?:(?!\*/).)*?\*/)\s*public', но обнаружил, что не найду методов без аннотаций (второй комментарий опущен во входных данных примера), несмотря на наличие ?: в начале регулярного выражения. Что случилось, пожалуйста? - person user3853423; 01.08.2014
comment
@ user3853423: Я думал, это то, что вы хотели. ?: не означает необязательный; это означает отсутствие захвата. Если вы хотите сделать его необязательным, поставьте ? после группы. - person user2357112 supports Monica; 01.08.2014

Я думаю, вы пытаетесь получить это,

>>> text = """ /** * comment */ class MyClass extens Base { /** * comment */ public function xyz """
>>> m = re.findall(r'\/\*\*(?:(?!\*\/).)*\*\/\s*public', text, re.DOTALL)
>>> m
['/** * comment */ public']

Если вы не хотите public в финальном совпадении, используйте приведенное ниже регулярное выражение, которое использует положительный просмотр вперед,

>>> m = re.findall(r'\/\*\*(?:(?!\*\/).)*\*\/(?=\s*public)', text, re.DOTALL)
>>> m
['/** * comment */']
person Avinash Raj    schedule 31.07.2014
comment
Извините, что смущаю вас. Когда перед public нет аннотации, текст public function xyz должен быть найден, потому что я также анализирую заголовок метода. - person user3853423; 31.07.2014
comment
пожалуйста, опубликуйте фактический ввод в вашем вопросе. - person Avinash Raj; 31.07.2014
comment
Спасибо за вашу готовность, пользователь 3853423 уже ответил на мой вопрос. - person user3853423; 31.07.2014
comment
Я думаю, он просто объяснил мое регулярное выражение, вот и все - person Avinash Raj; 31.07.2014

Вы должны быть в состоянии использовать это:

\/\*\*([^*]|\*[^/])*?\*\/\s*public

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

Пример: http://regexr.com/398b3

Пояснение: http://tinyurl.com/lcewdmo

Отказ от ответственности. Если комментарий содержит */, это не сработает.

person Mowday    schedule 31.07.2014