Есть ли способ сопоставить набор групп в любом порядке в регулярном выражении? - PullRequest
0 голосов
/ 30 августа 2010

Я просмотрел связанные вопросы, их было немало, но я не думаю, что кто-то ответил на этот вопрос. Я очень новичок в Regex, но я пытаюсь поправиться, поэтому терпите меня, пожалуйста. Я пытаюсь сопоставить несколько групп в строке, но в любом порядке. Это то, что я должен использовать Regex для? Если так, то как? Если это имеет значение, я планирую использовать их в IronPython.

РЕДАКТИРОВАТЬ: Кто-то попросил меня быть более конкретным, поэтому здесь:

Я хочу использовать re.match с регулярным выражением типа:

\[image\s*(?(@alt:(?<alt>.*?);).*(@title:(?<title>.*?);))*.*\](?<arg>.*?)\[\/image\]

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

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

[image @alt:alien; @title:reddit alien;]http://www.reddit.com/alien.png[/image]

Но у меня не должно быть проблем с соответствием:

[image @title:reddit alien; @alt:alien;]http://www.reddit.com/alien.png[/image]

Таким образом, «атрибуты» (вещи, которые находятся между «@» и «;» в первом «теге») должны совпадать в любом порядке, пока они оба появляются.

Ответы [ 2 ]

2 голосов
/ 30 августа 2010

Ответом на вопрос в вашем заголовке является «нет» - чтобы сопоставить N групп «в любом порядке», регулярное выражение должно иметь «или» (функция | в шаблоне регулярных выражений) среди N! (N факториал) возможные перестановки групп, произведение всех целых чисел от 1 до N. Это число, которое растет чрезвычайно быстро - для N, равного 6, это уже 720, для 7, этопочти 5000, и так далее в головокружительном темпе - так что этот подход совершенно непрактичен для любого N, который на самом деле не крошечный.

Решений может быть много, в зависимости от того, что вы хотите, чтобы группы были разделеныс.Скажем, например, что вам все равно (если вам все равно, отредактируйте свой вопрос с лучшими характеристиками).

В этом случае, если совпадающие совпадения невозможны или вас устраивают, выделите N отдельнорегулярные выражения, по одному на группу - скажем, эти N скомпилированных объектов RE находятся в списке с именем grps, тогда

mos = [g.search(thestring) for g in grps]

- список объектов соответствия для групп (None для группы, котораяне совпадает)Со списком mos вы можете выполнять все виды проверок и / или дальнейших манипуляций, например, all(mos) равен True тогда и только тогда, когда все группы совпадают, в этом случае [m.group() for m in mos] - это список подстрок, которые имеютбыли сопоставлены и т. д. и т. п.

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

Итак, сначала отредактируйте свой вопрос с более точными спецификациями, а затем, возможно, что-то прояснится, чтобы предоставить вамкод и / или алгоритмы, которые вам нужны.

Редактировать : Я вижу, что ОП теперь прояснил вопрос, по крайней мере, в части предоставления примера - хотя, смущая, он предлагаетПример шаблона RE и пример строки, которые должны не совпадать, независимо от упорядочения (RE указывает на наличие подстроки @title, которую в примере строки не имеет - озадачивает!).

Во всяком случае, если количество групп в примере (две, которые кажутся взаимозаменяемыми, одна, которая, кажется,Если это произойдет в определенном месте) является представителем реальных проблем ОП, то общее число представляющих интерес перестановок равно только двум, поэтому объединение «только двух» перестановок с вертикальной чертой | было бы, конечно, вполне осуществимым.Правда ли это в реальных проблемах ОП ...?

Редактировать : если число интересующих перестановок ничтожно, вот пример одного из способов избежать проблемыповторяющиеся имена групп в шаблоне (синтаксис требует Python 2.7 или выше, но это только для окончательного «понимания слова») - та же функциональность доступна во многих предыдущих версиях Python, только с менее элегантным синтаксисом dict(('a', ... ;-)...:

>>> r = re.compile(r'(?P<a1>a.*?a).*?(?P<b1>b.*?b)|(?P<b2>b.*?b).*?(?P<a2>a.*?a)')
>>> m = r.search('zzzakkkavvvbxxxbnnn')
>>> g = m.groupdict()
>>> d = {'a':(g.get('a1') or g.get('a2')), 'b':(g.get('b1') or g.get('b2'))}
>>> d
{'a': 'akkka', 'b': 'bxxxb'}
0 голосов
/ 30 августа 2010

Это очень похоже на одну из ключевых проблем использования регулярных выражений для разбора HTML - не требуется, чтобы атрибуты всегда указывались в одном и том же порядке, а многие теги имеют неожиданные атрибуты (например, <br clear="all">. вы работаете с очень похожим синтаксисом разметки.

Pyparsing решает эту проблему косвенным путем - вместо того, чтобы пытаться анализировать все различные перестановки, анализируйте общий "@attrname: attribute value;" синтаксис и отслеживать ключи и значения атрибутов в структуре данных отображения атрибутов. Сопоставление позволяет легко получить атрибут title, независимо от того, был он первым или последним в теге изображения. Это поведение встроено в методы API pyparsing, makeHTMLTags и makeXMLTags.

Конечно, эта разметка , а не XML, но подобный подход позволяет довольно легко работать с результатами:

text = """[image @alt:alien; @title:reddit alien;]http://www.reddit.com/alien1.png[/image]

But I should have no problem matching:

[image @title:reddit alien; @alt:alien;]http://www.reddit.com/alien2.png[/image]
"""

from pyparsing import Suppress, Group, Word, alphas, SkipTo, Dict, ZeroOrMore

LBRACK,RBRACK,COLON,SEMI,AT = map(Suppress,"[]:;@")
tagAttribute = Group(AT + Word(alphas) + COLON + SkipTo(SEMI) + SEMI)
imageTag = LBRACK + "image" + Dict(ZeroOrMore(tagAttribute)) + RBRACK
imageLink = imageTag + SkipTo("[/image]")("text")

for taginfo in imageLink.searchString(text):
    print taginfo.alt
    print taginfo.title
    print taginfo.text
    print

Печать:

alien
reddit alien
http://www.reddit.com/alien1.png

alien
reddit alien
http://www.reddit.com/alien2.png
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...