2016-06-20 15 views
12

Cel: Próbuję wykonać cięcie w języku Python RegEx, w którym podział nie wykonuje dokładnie tego, co chcę. Muszę przeciąć wzór, ale między postaciami.Wycinanie we wzorze za pomocą Python regex

Co szukam:

muszę rozpoznać wzór poniżej w łańcuchu, i podzielić ciąg w miejscu rury. Rura nie znajduje się w łańcuchu, pokazuje tylko to, co chcę podzielić.

Wzór: CDE|FG

String: ABCDEFGHIJKLMNOCDEFGZYPE

Wyniki: ['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE']

Co próbowałem:

I wydaje się, że za pomocą split z nawiasu jest blisko, ale nie robi” t zachowaj wzór wyszukiwania dołączony do wyników, tak jak tego potrzebuję.

re.split('CDE()FG', 'ABCDEFGHIJKLMNOCDEFGZYPE')

Daje,

['AB', 'HIJKLMNO', 'ZYPE']

Kiedy rzeczywiście trzeba,

['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE']

Motywacja:

Ćwicząc z RegEx, i chciałem zobaczyć, czy mógłbym użyć RegEx do stworzenia skryptu, który przewidywałby fragmenty trawienia białka przy użyciu konkretnych proteaz.

+0

Czy rozwiązanie oparte non-regex do przyjęcia dla Ciebie? – wim

+0

Absolutnie! Jednak szykowałem się do elegancji. Mogę to zrobić ręcznie z akapitem kodu Pythona. –

+0

Czego potrzebujesz to split z '(? <= CDE) (? = FG)' – sln

Odpowiedz

7

Non regex sposobem byłoby replace wzór z wartością rurami a następnie split.

>>> pattern = 'CDE|FG' 
>>> s = 'ABCDEFGHIJKLMNOCDEFGZYPE' 
>>> s.replace('CDEFG',pattern).split('|') 
['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE'] 
+1

W przeciwieństwie do wyrażenia regularnego, pozwala to na łatwe podzielenie ciągu na wiele różnych wzorów. Jednakże, jeśli wprowadzony znak kontrolny jest już używany w pliku, wynik będzie niepotrzebny. (w tym przypadku rura) –

+2

@Yab Dokładnie. Odpowiedź jest * szybszą * alternatywą dla wyrażenia regularnego. PO wspomina, że ​​są one otwarte na odpowiedzi nieregresowe w komentarzu, a tym samym odpowiedź. Zakłada się, że * pipe * tutaj nie występuje w zbiorze danych. Zwykle w takich przypadkach używane jest wiele separatorów zawierających znaki specjalne i literały Unicode. Nie wykorzystałem tego do zademonstrowania prostego przykładu użycia. –

+1

W przypadkach, w których _edujesz_ wyrażenie regularne, możesz użyć tego samego podejścia z 're.sub'. Na przykład "re.sub (r" (CD [xy]) (FG) ", r" \ 1 | \ 2 ", dane)'. – alexis

5

Można go rozwiązać z re.split() i pozytywnej "look arounds":

>>> re.split(r"(?<=CDE)(\w+)(?=FG)", s) 
['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE'] 

pamiętać, że jeśli jedna z sekwencji ciętych jest pusty ciąg znaków, co można uzyskać ciąg pusty wewnątrz otrzymanej listy. Można sobie z tym poradzić „ręcznie”, próbkę (muszę przyznać, że nie jest tak całkiem):

import re 

s = "ABCDEFGHIJKLMNOCDEFGZYPE" 

cut_sequences = [ 
    ["CDE", "FG"], 
    ["FGHI", ""], 
    ["", "FGHI"] 
] 

for left, right in cut_sequences: 
    items = re.split(r"(?<={left})(\w+)(?={right})".format(left=left, right=right), s) 

    if not left: 
     items = items[1:] 

    if not right: 
     items = items[:-1] 

    print(items) 

Wydruki:

['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE'] 
['ABCDEFGHI', 'JKLMNOCDEFGZYPE'] 
['ABCDE', 'FGHIJKLMNOCDEFGZYPE'] 
+0

Podoba mi się to rozwiązanie dużo, i robi to, o co prosiłem, ale kiedy próbuję uogólnić, nie mogę uzyskać takiej sekwencji, by działało '| FGHI'. –

+0

@MichaelMolter tak, otrzymasz dodatkowy pusty łańcuch jako pierwszy podzielony element, prawda? Obawiam się, że musiałbyś obsłużyć puste przypadki rozdzielonych ograniczników, takie jak '| FGHI' lub' FGHI | 'ręcznie krojące wynik' re.split() ':' [1:] 'i' [: -1] 'odpowiednio. Być może istnieje bardziej elegancki sposób, aby obsłużyć to .. dzięki. – alecxe

+0

To rozwiązanie jest takie błędne: Szczęśliwie podzieli "ABCDExxxxxxxFGH" na trzy części i nie będzie poprawnie podzielone, jeśli istnieją trzy poprawne punkty cięcia, itp. – alexis

1

Bezpieczniejsze rozwiązanie non-regex może być to:

import re 

def split(string, pattern): 
    """Split the given string in the place indicated by a pipe (|) in the pattern""" 
    safe_splitter = "#@#@[email protected]#@#" 
    safe_pattern = pattern.replace("|", safe_splitter) 
    string = string.replace(pattern.replace("|", ""), safe_pattern) 
    return string.split(safe_splitter) 

s = "ABCDEFGHIJKLMNOCDEFGZYPE" 
print(split(s, "CDE|FG")) 
print(split(s, "|FG")) 
print(split(s, "FGH|")) 

https://repl.it/C448

2

Aby zachować wzór łupania kiedy rozstał się z re.split, lub jego części, należy ująć je w nawiasach.

>>> data 
'ABCDEFGHIJKLMNOCDEFGZYPE' 
>>> pieces = re.split(r"(CDE)(FG)", data) 
>>> pieces 
['AB', 'CDE', 'FG', 'HIJKLMNO', 'CDE', 'FG', 'ZYPE'] 

Wystarczająco łatwe. Wszystkie części są tam, ale jak widać, zostały rozdzielone. Więc musimy je ponownie złożyć. To trudniejsza część. Przyjrzyj się uważnie, a zobaczysz, że musisz dołączyć do dwóch pierwszych części, dwóch ostatnich części, a reszta do trzech. Upraszczam kod, wypełniając listę, ale można to zrobić z oryginalną listą (i odrobiną dodatkowego kodu), jeśli wydajność jest problemem.

>>> pieces = [""] + pieces 
>>> [ "".join(pieces[i:i+3]) for i in range(0,len(pieces), 3) ] 
['ABCDE', 'FGHIJKLMNOCDE', 'FGZYPE'] 

re.split() gwarantuje kawałek dla każdego ujęcia (w nawiasach) grupy, plus kawałek za to, co jest między nimi. W przypadku bardziej złożonych wyrażeń regularnych, które wymagają własnej grupy, należy używać grup niezapisujących, aby zachować format zwracanych danych. (W przeciwnym razie musisz dostosować krok ponownego składania).

PS. Podoba mi się również sugestia Bhargava Rao, aby wstawić znak separatora w ciągu znaków. Jeśli wydajność nie jest problemem, myślę, że to kwestia gustu.

Edit: Oto (mniej przezroczysty) sposób to zrobić bez dodawania pusty ciąg do listy:

pieces = re.split(r"(CDE)(FG)", data) 
result = [ "".join(pieces[max(i-3,0):i]) for i in range(2,len(pieces)+2, 3) ]