Regex для текстовых файлов с частично неполной вложенной структурой - PullRequest
0 голосов
/ 28 января 2019

Я хочу проанализировать файл с этим вложенным форматом:

/begin FUNCTION

    foo
    "1.2.12 foo_long"

    /begin DEF1
    /end DEF1
    FUNCTION_VERSION "1.2.0"

/end FUNCTION

/begin FUNCTION

    bar
    ""

/end FUNCTION

/begin FUNCTION

    urbi
    "10.15.23 urbi_long"

    /begin DEF1
    /end DEF1
    FUNCTION_VERSION "10.15.3"

/end FUNCTION

Из этого я хочу извлечь имена функций, длинные имена и номер версии.

Я делаю это сследующее регулярное выражение:

sSearch = r'/begin FUNCTION\s+(\w*)\s+"[\d\._\s]*([^"]+)*"(.*?)FUNCTION_VERSION\s+"([^"]+)"\s+/end FUNCTION'
lMatches = re.findall(sSearch, sFileContent, re.S)
dMatches = {args[0]: [args[3], args[1]] for args in lMatches if args}
print(dMatches)

Это приводит к:

{'foo': ['1.2.0', 'foo_long'], 'bar': ['10.15.3', '']}

Версия функции из urbi ошибочно присвоена bar.Я не хочу, чтобы bar возвращался вообще, поскольку он не содержит версию функции.

Как я могу адаптировать регулярное выражение, которое выпускает вхождение /begin FUNCTION до bar, когда end FUNCTION найден без ведущего function version?

Я бы хотел, чтобы вывод был:

{'foo': ['1.2.0', 'foo_long'], 'urbi': ['10.15.3', 'urbi_long']}

PS Что меня также смущает, так это то, почему мне нужно добавить ненужную группу захвата (.*?) в середине.Разве это не должно работать и с простым .*?

Ответы [ 3 ]

0 голосов
/ 28 января 2019

Вы можете сделать это, используя negative lookahead следующим образом:

import re

with open('filename.txt') as fd:
    data = fd.read()

regex = re.compile(
    r'begin\s+FUNCTION\s+([a-zA-Z_]+)\s+'
    r'(?:"[\d.]+\d\s+([a-zA-Z_]+)")?'
    r'(?:(?:(?!/end\s+FUNCTION).)+FUNCTION_VERSION\s+"([\d.]+\d)")?',
    re.MULTILINE | re.DOTALL
)
result = {i[0]: [i[2], i[1]] for i in regex.findall(data)}
print(result)

#outpout
{'urbi': ['10.15.3', 'urbi_long'], 'foo': ['1.2.0', 'foo_long'], 'bar': ['', '']}

#refine result
result = {k: [i for i in v if i] for k, v in result.items()}
print(result)

# output
{'urbi': ['10.15.3', 'urbi_long'], 'foo': ['1.2.0', 'foo_long'], 'bar': []}
0 голосов
/ 28 января 2019

Попробуйте это регулярное выражение:

\/begin FUNCTION\s*(\w+)\s*"(?:(?!end FUNCTION)[\s\S])*?(\w+)"[\s\S]*?FUNCTION_VERSION\s*"([^"\s]+)"

Нажмите для демонстрации

Объяснение:

  • \/begin FUNCTION\s* - соответствует /begin FUNCTION, за которым следуют 0+ пробелов
  • (\w+) - соответствует 1+ вхождению символа слова и захватывает его в группе 1. Это имя вашей функции
  • \s*" - соответствует 0+ пробелов, за которыми следует "
  • (?:(?!end FUNCTION)[\s\S])*? - закаленный жадный жетон, который будет соответствовать любому символу, пока он не начнется с фразы end FUNCTION
  • (\w+) - соответствует 1+ символам слова и сохраняет его в группе 2. Это захватывает ваше длинное имя
  • "[\s\S]*?FUNCTION_VERSION\s*" - соответствует ", за которым следует 0+ вхождений любого символа, за которыми следует FUNCTION_VERSION, за которым следует0+ пробелов, за которыми следует "
  • ([^"\s]+) - соответствует 1+ вхождениям любого символа, который не является ни ", ни пробелом.Это записано в группе 3 и содержит номер вашей версии.
  • " - соответствует "
0 голосов
/ 28 января 2019

Это один подход с использованием Lookbehind & Lookahead.

Демонстрация:

import re

s = """/begin FUNCTION

    foo
    "1.2.0 foo_long"

    /begin DEF1
    /end DEF1
    FUNCTION_VERSION "1.2.0"

/end FUNCTION

/begin FUNCTION

    bar
    ""

/end FUNCTION

/begin FUNCTION

    urbi
    "10.15.3 urbi_long"

    /begin DEF1
    /end DEF1
    FUNCTION_VERSION "10.15.3"

/end FUNCTION"""

result = {}
for i in re.findall(r"(?<=/begin FUNCTION)(.*?)(?=/end FUNCTION)", s, flags=re.DOTALL):
    val = i.strip().splitlines()
    if val:
        try:
            result[val[0]] = val[1].replace('"', "").split()
        except:
            result[val[0]] = []
print(result)

Вывод:

{'urbi': ['10.15.3', 'urbi_long'], 'foo': ['1.2.0', 'foo_long'], 'bar': []}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...