Python Regex - нежадное сопоставление не работает

У меня есть плоский файл с одним именем функции C ++ и частью ее объявления следующим образом:

virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const
void function_name2
void NameSpace2::NameSpace4::ClassName2::function_name3
function_name4

Я пытаюсь извлечь только имена функций, используя эту строку:

fn_name = re.match(":(.*?)\(?", lines)

Я могу понять, почему function_name2 и function_name4 не совпадают (потому что нет ведущего :. Но я вижу, что даже для function_name1 и function_name3 он не выполняет нежадное совпадение. Результатом fn_name.group() является

:NameSpace2::ClassName1::function_name1

У меня три вопроса:

  1. Я ожидал, что из строки 1 будет извлечена только строка «function_name1», но нежадное совпадение, похоже, не работает. Почему?
  2. Почему не извлекается строка 3?
  3. Как получить имена функций из всех строк с помощью одного регулярного выражения?

Пожалуйста помоги.


person Karthick S    schedule 19.03.2016    source источник
comment
знаете ли вы, что re.match соответствует, начиная с начала строки? Ни одна из ваших строк не начинается с двоеточия. Вы пробовали вместо этого re.search?   -  person Bryan Oakley    schedule 19.03.2016
comment
Также ленивое сопоставление не влияет на место, где регулярное выражение начинает сопоставление - оно влияет только на конец сопоставления   -  person Sebastian Proske    schedule 19.03.2016


Ответы (4)


Это работает очень хорошо, по крайней мере, с вашим примером:

^(?:\w+ +)*(?:\w+::)*(\w+)

то есть в коде Python:

import re

function_name = re.compile(r'^(?:\w+ +)*(?:\w+::)*(\w+)', re.MULTILINE)
matches = function_name.findall(your_txt)

# -> ['function_name1', 'function_name2', 'function_name3', 'function_name4']

Вывод: если вы можете сделать это с помощью жадного сопоставления, делайте это с помощью жадного сопоставления.


Обратите внимание, что \w неверно для идентификатора C, но записать технически правильный символьный класс, соответствующий ему, - это не вопрос. Найдите и используйте правильный набор символов вместо \w.

person Tomalak    schedule 19.03.2016

1) Всегда используйте r" " строки для регулярных выражений.

2)

Я пытаюсь извлечь только имена функций, используя эту строку:

fn_name = re.match(":(.*?)\(?", lines)

Результатом fn_name.group () будет

:NameSpace2::ClassName1::function_name1

Я этого не вижу:

import re

line = "virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const"

fn_name = re.search(r":(.*?)\(?", line)
print(fn_name.group())

--output:--
:

В любом случае, если вы хотите увидеть, как работает нежадный режим, посмотрите на этот код:

import re

line = "N----1----2"

greedy_pattern = r"""
    N
    .*
    \d
"""

match_obj = re.search(greedy_pattern, line, flags=re.X)
print(match_obj.group())

non_greedy_pattern = r"""
    N
    .*?
    \d
"""
match_obj = re.search(non_greedy_pattern, line, flags=re.X)
print(match_obj.group())

--output:--
N----1----2
N----1

Нежадная версия запрашивает все символы, совпадающие с .*, до первой встреченной цифры, в то время как жадная версия пытается найти самое длинное совпадение для .*, за которым следует цифра.

3) Внимание! Нет зоны регулярных выражений!

func_names = [
"virtual void NameSpace1::NameSpace2::ClassName1::function_name1(int arg1) const",
"void function_name2",
"void NameSpace2::NameSpace4::ClassName2::function_name3",
"function_name4",
]

for func_name in func_names:
    name = func_name.rsplit("::", 1)[-1]

    pieces = name.rsplit(" ", 1)

    if pieces[-1] == "const":
        name = pieces[-2]
    else:
        name = pieces[-1]

    name = name.split('(', 1)[0]
    print(name)

--output:--
function_name1
function_name2
function_name3
function_name4
person 7stud    schedule 19.03.2016

  1. Я ожидал, что из строки 1 будет извлечена только строка "имя_функции1", но не жадное совпадение, похоже, не работает. Почему?

Это результат вашего регулярного выражения ": (. *?) \ (?"

Я думаю, что ваше регулярное выражение "слишком лениво". Он будет соответствовать только :, потому что (.*?) обозначает соответствие любых символов "как можно меньше", тогда механизм регулярных выражений выбирает совпадение с нулевым символом. Он не будет соответствовать до \(?, как вы и ожидали, потому что ? просто означает "необязательный".

  1. Почему не извлекается строка 3?

Поскольку я тестировал ваше регулярное выражение. Не работает вообще не только третья строчка.

  1. Как получить имена функций из всех строк с помощью одного регулярного выражения?

Вы можете начать с этого минимального примера

(?:\:\:|void\s+)(\w+)(?:\(|$)|(function_name4)

Где (?:\:\:|void\s+) обозначает все, что предшествует имени вашей функции, а (?:\(|$) обозначает все, что следует за именем функции.

Обратите внимание, что function_name4 предполагается объявить явно из-за отсутствия шаблона.

см .: ДЕМО

person fronthem    schedule 19.03.2016

Я раньше сталкивался с чем-то подобным, когда пытался захватить «N ---- 1» из «N foo bar N ---- 1 ---- 2». Добавление ведущего. * Дало желаемый результат.

import re
line = "N foo bar N----1----2"
match_obj = re.search(r'(N.*?\d)', line)
print(match_obj.group(1)) 

match_obj = re.search(r'.*(N.*?\d)', line)
print(match_obj.group(1))

--output:--
N foo bar N----1
N----1
person shao.lo    schedule 13.02.2017