Biorąc pod uwagę ciąg s = "<foo>abcaaa<bar>a<foo>cbacba<foo>c" Próbuję napisać wyrażenie regularne, które wyodrębni fragmenty: nawiasy kątowe z tekstem w środku i otaczającym go tekstem. Lubię to:

<foo>abcaaa
abcaaa<bar>a
a<foo>cbacba
cbacba<foo>c

Tak więc oczekiwany wynik powinien wyglądać następująco:

["<foo>abcaaa", "abcaaa<bar>a", "a<foo>cbacba", "cbacba<foo>c"]

Znalazłem to pytanie Jak znaleźć pokrywające się dopasowania z wyrażeniem regularnym? co nieco przybliżyło mnie do pożądanego rezultatu, ale mimo to moje wyrażenie regularne nie działa.

regex = r"(?=([a-c]*)\<(\w+)\>([a-c]*))"

Jakieś pomysły, jak rozwiązać ten problem?

2
MrZH6 2 kwiecień 2020, 01:20

3 odpowiedzi

Najlepsza odpowiedź

Możesz dopasować pokrywającą się zawartość do standardowej składni wyrażenia regularnego, używając grup przechwytywania wewnątrz asercji wyszukiwania, ponieważ mogą one pasować do części ciągu bez zużywania dopasowanego podciągu, a tym samym wykluczając go z dalszych dopasowań. W tym konkretnym przykładzie dopasowujemy początek ciągu lub > jako kotwicę dla asercji lookahead, która przechwytuje nasze rzeczywiste cele:

(?:\A|>)(?=([a-c]*<\w+>[a-c]*))

Zobacz demo wyrażeń regularnych.

W Pythonie używamy następnie właściwości re.findall(), aby zwracać tylko dopasowania przechwycone w grupach, gdy grupy przechwytywania są obecne w wyrażeniu:

text = '<foo>abcaaa<bar>a<foo>cbacba<foo>c'
expr = r'(?:\A|>)(?=([a-c]*<\w+>[a-c]*))'
captures = re.findall(expr, text)
print(captures)

Wynik:

['<foo>abcaaa', 'abcaaa<bar>a', 'a<foo>cbacba', 'cbacba<foo>c']
2
oriberu 1 kwiecień 2020, 23:01

Możesz użyć tego kodu wyrażenia regularnego w Pythonie:

>>> s = '<foo>abcaaa<bar>a<foo>cbacba<foo>c'
>>> reg = r'([^<>]*<[^>]*>)(?=([^<>]*))'
>>> print ( [''.join(i) for i in re.findall(reg, s)] )
['<foo>abcaaa', 'abcaaa<bar>a', 'a<foo>cbacba', 'cbacba<foo>c']

RegEx Demo

Szczegóły wyrażenia regularnego:

  • ([^<>]*<[^>]*>): Przechwyć grupę 1, aby dopasować 0 lub więcej znaków, które nie są < i >, po których następuje <...> ciąg.
  • (?=([^<>]*)): Lookahead, aby potwierdzić, że mamy 0 lub więcej znaków innych niż <> przed bieżącą pozycją. Mamy przechwyconą grupę # 2 wewnątrz tego lookahead.
2
anubhava 1 kwiecień 2020, 23:01

Musisz ustawić granice po lewej i prawej stronie na < lub > znaki lub początek / koniec ciągu.

Posługiwać się

import re
text = "<foo>abcaaa<bar>a<foo>cbacba<foo>c"
print( re.findall(r'(?=(?<![^<>])([a-c]*<\w+>[a-c]*)(?![^<>]))', text) )
# => ['<foo>abcaaa', 'abcaaa<bar>a', 'a<foo>cbacba', 'cbacba<foo>c']

Zobacz demo Pythona online i demo wyrażenia regularnego.

Szczegóły wzoru

  • (?= - początek pozytywnego wyprzedzenia, aby umożliwić nakładanie się dopasowań
    • (?<![^<>]) - początek ciągu, < lub >
    • ([a-c]*<\w+>[a-c]*) - Grupa 1 (wyodrębniona wartość): 0+ a, b lub c znaków, a następnie <, ponad 1 słowo znaków, > i ponownie 0+ a, b lub c znaków
    • (?![^<>]) - koniec ciągu, < lub > musi nastąpić natychmiast
  • ) - koniec antycypowania.
2
Wiktor Stribiżew 1 kwiecień 2020, 22:48