2012-01-26 10 views
5

Próbuję parsować niektóre docstrings.regex: string z opcjonalnymi częściami

przykładem docstrings jest:

Test if a column field is larger than a given value 
    This function can also be called as an operator using the '>' syntax 

    Arguments: 
     - DbColumn self 
     - string or float value: the value to compare to 
      in case of string: lexicographic comparison 
      in case of float: numeric comparison 
    Returns: 
     DbWhere object 

Zarówno Arguments i Returns części są opcjonalne. Chcę, aby moje wyrażenie regularne zwracało jako grupy opis (pierwsze wiersze), część Arguments (jeśli jest obecna) i część Returns (jeśli jest obecna).

regex mam teraz jest:

m = re.search('(.*)(Arguments:.*)(Returns:.*)', s, re.DOTALL) 

i pracuje w przypadku wszystkie trzy części są obecne, ale nie tak szybko, jak Arguments lub części Returns nie są dostępne. Próbowałem kilku wariacji z nie wymagającymi chciwości modyfikatorami, takimi jak ??, ale bezskutecznie.

Edit: Kiedy Arguments i Returns części są obecne, tak naprawdę chciałby tylko, aby dopasować tekst po Arguments: i Returns: odpowiednio.

Dzięki!

+1

Czy zamówienie jest zawsze stałe? I. e., Jest "Arguments" zawsze po standardowym tekście i przed 'Returns'? –

+0

Tak, zamówienie jest zawsze stałe. – BioGeek

Odpowiedz

7

spróbuj:

re.search('^(.*?)(Arguments:.*?)?(Returns:.*)?$', s, re.DOTALL) 

tylko co drugi i trzeci grup opcjonalnie przez dodanie ?, a co kwalifikatorów dwóch pierwszych grup non-chciwy przez (znowu) dołączenie ? na nich (tak, mylące).

Ponadto, jeśli użyjesz nieagrypcyjnego modyfikatora w pierwszej grupie wzorca, dopasuje on najkrótszy możliwy podciąg, który dla .* jest pusty. Możesz to przezwyciężyć, dodając znak końca wiersza ($) na końcu wzorca, który zmusza pierwszą grupę do dopasowania jak najmniejszej liczby znaków, aby spełnić wzór, tj. Cały ciąg, gdy nie ma wartości ArgumentsReturns sekcje i wszystko przed tymi sekcjami, jeśli są obecne.

Edit: OK, jeśli chcesz po prostu uchwycić tekst po z Arguments: i Returns: żetonów, trzeba będzie schować się jeszcze kilka grup. My nie zamierzamy korzystać ze wszystkich grup, więc nazywanie ich -z notacji <?P<name> (innego znaku pytanie, argh!) - zaczyna mieć sens:

>>> m = re.search('^(?P<description>.*?)(Arguments:(?P<arguments>.*?))?(Returns:(?P<returns>.*))?$', s, re.DOTALL) 
>>> m.groupdict()['description'] 
"Test if a column field is larger than a given value\n This function can also be called as an operator using the '>' syntax\n\n " 
>>> m.groupdict()['arguments'] 
'\n  - DbColumn self\n  - string or float value: the value to compare to\n   in case of string: lexicographic comparison\n   in case of float: numeric comparison\n ' 
>>> m.groupdict()['returns'] 
'\n  DbWhere object' 
>>> 
+0

Działa jak urok! Jak zmodyfikowałbyś wyrażenie regularne, jeśli dla części opcjonalnych chciałbym tylko dopasować tekst po 'Arguments' i' Returns'? – BioGeek

+0

Coś jak 're.search ('^ (. *?) (Argumenty: (. *?))? (Zwraca: (. *))? $', Doc, re.DOTALL)' działa, ale ja nie " t dba o drugą i czwartą grupę, którą zwraca. – BioGeek

+0

Edytowałem swoją odpowiedź. Po prostu nazwij grupy i zapomnij o 'groups()', zamiast tego użyj 'groupdict()'. – Chewie

3

Jeśli chcesz, aby dopasować tekst po opcjonalnych odcinkach Arguments: i , nie chcesz używać (?P<name>...) do nadawania nazw grupom przechwytywania, możesz także użyć, (?:...), niezapisującej wersji zwykłych nawiasów.

regex będzie wyglądać następująco:

m = re.search('^(.*?)(?:Arguments:(.*?))?(?:Returns:(.*?))?$', doc, re.DOTALL) 
#      ^^     ^^ 

Według Python3 documentation:

(?:...)

Non-przechwytywanie wersja regularnych nawiasach. Pasuje do dowolnego wyrażenia regularnego w nawiasach, ale podciągu dopasowanego przez grupę nie można odzyskać po wykonaniu dopasowania lub odwołaniu się do niego później.