2016-06-20 68 views
12

Obiettivo: Sto tentando di eseguire un taglio in Python RegEx dove split non fa esattamente ciò che voglio. Ho bisogno di tagliare all'interno di un modello, ma tra i personaggi.Tagliare all'interno di un pattern usando Python regex

Quello che sto cercando:

Ho bisogno di riconoscere il modello di seguito in una stringa, e dividere la stringa in corrispondenza della posizione del tubo. La pipa non è effettivamente nella stringa, mostra solo dove voglio dividere.

Pattern: CDE|FG

String: ABCDEFGHIJKLMNOCDEFGZYPE

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

Quello che ho cercato:

Mi sembra che usando split con parentesi è vicino, ma doesn' t mantenere il modello di ricerca associato ai risultati come se ne avesse bisogno.

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

dona,

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

Quando ho effettivamente bisogno,

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

Motivazione:

Esercitarsi con RegEx e volevo vedere se potevo usare RegEx per creare uno script che prevedesse i frammenti di una digestione proteica utilizzando proteasi specifiche.

+0

è una soluzione a base non-regex accettabile per te? – wim

+0

Assolutamente! Tuttavia, stavo andando per l'eleganza. Posso farlo manualmente con un paragrafo di codice Python. –

+0

Quello che ti serve è una divisione con '(? <= CDE) (? = FG)' – sln

risposta

7

modo non regex sarebbe quella di replace il modello con il valore in filodiffusione e quindi split.

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

A differenza della regex, questo ti permette di dividere facilmente la stringa su molti modelli diversi. Tuttavia, produrrà un risultato indesiderato se il carattere di controllo introdotto è già in uso nel file. (in questo caso, la pipa) –

+2

@Yab Esattamente. La risposta è un'alternativa * più veloce * alla regex. L'OP afferma che sono aperti alle risposte non regex in un commento e quindi alla risposta. Si presume che * pipe * non sia presente nel set di dati. Solitamente in questi casi viene utilizzato un delimitatore multiplo contenente caratteri speciali e letterali Unicode. Non l'ho usato per dimostrare un caso d'uso semplice. –

+1

Per i casi in cui _need_ un'espressione regolare, è possibile utilizzare lo stesso approccio con 're.sub'. Ad esempio, 're.sub (r" (CD [xy]) (FG) ", r" \ 1 | \ 2 ", dati)'. – alexis

5

È possibile risolverlo con re.split() e positivo "look arounds":

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

Si noti che se una delle sequenze di taglio è una stringa vuota, si otterrebbe una stringa vuota all'interno della lista risultante. È possibile gestire che "manualmente", del campione (lo ammetto, non è che abbastanza):

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) 

Stampe:

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

Mi piace questo soluzione molto, e fa quello che ho chiesto, ma quando cerco di generalizzare, non riesco a ottenere una sequenza di taglio come questa per funzionare '| FGHI'. –

+0

@MichaelMolter sì, otterrai la stringa extra vuota come primo elemento di divisione, giusto? Temo che dovresti gestire il caso di delimitatori di split vuoto come '| FGHI' o' FGHI | 'tagliando manualmente il risultato di 're.split()': '[1:]' e '[: -1] 'rispettivamente. Potrebbe esserci un modo più elegante per gestire quello ... grazie. – alecxe

+0

Questa soluzione è così sbagliata: dividerà felicemente "" ABCDExxxxxxxFGH "' in tre pezzi, e * non * dividerà correttamente se ci sono tre punti di taglio corretti, ecc. – alexis

1

una soluzione non-regex più sicuro potrebbe essere questo:

import re 

def split(string, pattern): 
    """Split the given string in the place indicated by a pipe (|) in the pattern""" 
    safe_splitter = "#@#@SP[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

Per mantenere il modello di divisione quando si divide con re.split, o parti di esso, racchiuderli tra parentesi.

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

Abbastanza facile. Tutte le parti ci sono, ma come puoi vedere sono state separate. Quindi abbiamo bisogno di rimontarli. Questa è la parte più difficile. Guarda attentamente e vedrai che devi unire i primi due pezzi, gli ultimi due pezzi e il resto in triple. Semplifico il codice riempendo la lista, ma potresti farlo con la lista originale (e un po 'di codice extra) se le prestazioni sono un problema.

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

re.split() garantisce un pezzo per ogni gruppo di cattura (tra parentesi), più un pezzo di quello che c'è tra. Con espressioni regolari più complesse che richiedono il proprio raggruppamento, utilizzare gruppi non acquisibili per mantenere lo stesso formato dei dati restituiti. (In caso contrario sarà necessario adattare la fase di riassemblaggio.)

PS. Mi piace anche il suggerimento di Bhargav Rao di inserire un carattere separatore nella stringa. Se le prestazioni non sono un problema, immagino sia una questione di gusti.

Edit: Ecco un modo (meno trasparente) di farlo senza l'aggiunta di una stringa vuota alla lista:

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