Невозможно получить регулярное выражение для захвата последней группы

#python #regex #bioinformatics

#python #регулярное выражение #биоинформатика

Вопрос:

Я пытаюсь написать регулярное выражение на python для анализа дерева Newick, но, хоть убей, я не могу привести его последнюю часть в соответствие. Мне нужно проанализировать три типа форматов Newick:

 ((A,B),C);
((A:0.1,B:0.2),C:0.3);
((A:[c1]0.1,B:[c2]0.2),C:[c2]0.3);
 

… каждый из которых содержит три метки (A, B, C) и различные другие биты информации. Я хочу получить три метки. Вот мое регулярное выражение:

 regex = re.compile(r"""
(
    ([,(])              # boundary
    ([A-Z0-9_-.] )    # label
    (:)?                # optional colon
    ([. ?])?          # optional comment chunk
    (d .d )?         # optional branchlengths
    ([),])              # end!
)
""", re.IGNORECASE   re.VERBOSE   re.DOTALL)
 

… тем не менее, я получаю только A и C. Никогда B. Я отследил сбой до последней захваченной группы ([),]) — если я удалю это, я получу все A, B и C. Пожалуйста, помогите — что здесь не так ?!

Ответ №1:

Проблема, вероятно, в том, что вы ищете неперекрывающиеся экземпляры регулярного выражения. Такие методы, как findall не будут возвращать B, поскольку совпадение для A потребляет , предыдущее B .

 >>> regex.findall("((A:[c1]0.1,B:[c2]0.2),C:[c2]0.3);")
[('(A:[c1]0.1,', '(', 'A', ':', '[c1]', '0.1', ','), (',C:[c2]0.3)', ',', 'C', ':', '[c2]', '0.3', ')')]
 

Изменение конечного шаблона для просмотра вперед (чтобы он ничего не потреблял) решает проблему.

 >>> regex = re.compile(r"""
... (
...     ([,(])              # boundary
...     ([A-Z0-9_-.] )    # label
...     (:)?                # optional colon
...     ([. ?])?          # optional comment chunk
...     (d .d )?         # optional branchlengths
...     (?=[),])            # end!
... )
... """, re.IGNORECASE   re.VERBOSE   re.DOTALL)
>>>
>>> regex.findall("((A:[c1]0.1,B:[c2]0.2),C:[c2]0.3);")
[('(A:[c1]0.1', '(', 'A', ':', '[c1]', '0.1'), (',B:[c2]0.2', ',', 'B', ':', '[c2]', '0.2'), (',C:[c2]0.3', ',', 'C', ':
', '[c2]', '0.3')]
>>>
 

В противном случае, вместо использования findall , вы можете использовать search итеративно и обезьянничать с pos аргументом.

Что-то вроде этого:

 >>> x = "((A:[c1]0.1,B:[c2]0.2),C:[c2]0.3);"
>>> r = []
>>> index = 0
>>> while True:
...     m = regex.search(x, index)
...     if not m:
...        break
...     r.append(m.groups())
...     index = m.end(7)-1
...
>>> r
[('(A:[c1]0.1,', '(', 'A', ':', '[c1]', '0.1', ','), (',B:[c2]0.2)', ',', 'B', ':', '[c2]', '0.2', ')'), (',C:[c2]0.3)',
 ',', 'C', ':', '[c2]', '0.3', ')')]
 

Ответ №2:

Если вам просто нужны метки, не могли бы вы просто использовать простое регулярное выражение, например [(,]([A-Z]) ?

 import re

text = ["((A,B),C);",
        "((A:0.1,B:0.2),C:0.3);",
        "((A:[c1]0.1,B:[c2]0.2),C:[c2]0.3);"]

for line in text:
    labels = re.findall(r'[(,]([A-Z])', line)
    print labels
 

Результат:

['A', 'B', 'C']
['A', 'B', 'C']
['A', 'B', 'C']

Комментарии:

1. Я хочу получить весь фрагмент (от начальной границы до конечной границы) и одновременно разобрать его на эти подразделы (начало, метка, комментарий, длины ветвей, конец).