2015-05-01 7 views
7

Con il modulo re, sembra che non sono in grado di dividere su partite di pattern che sono stringhe vuote:Python regex: suddivisione in corrispondenza modello che è una stringa vuota

>>> re.split(r'(?<!foo)(?=bar)', 'foobarbarbazbar') 
['foobarbarbazbar'] 

In altre parole, anche se un si trova la corrispondenza, se è la stringa vuota, anche re.split non può dividere la stringa.

Il docs for re.split sembra supportare i miei risultati.

A "soluzione" è stato abbastanza facile da trovare per questo caso particolare:

>>> re.sub(r'(?<!foo)(?=bar)', 'qux', 'foobarbarbazbar').split('qux') 
['foobar', 'barbaz', 'bar'] 

Ma questo è un modo soggetto ad errori di farlo perché poi devo diffidare di stringhe che già contengono la stringa che sto fessurazioni sulle:

>>> re.sub(r'(?<!foo)(?=bar)', 'qux', 'foobarbarquxbar').split('qux') 
['foobar', 'bar', '', 'bar'] 

c'è un modo migliore per dividere a una corrispondenza pattern vuoto con il modulo re? Inoltre, perché re.split non mi consente di farlo in primo luogo? So che è possibile con altri algoritmi split che funzionano con regex; ad esempio, sono in grado di farlo con il codice JavaScript incorporato String.prototype.split().

risposta

8

È spiacevole che il split richiede un match non larghezza zero, ma non è stato ancora a fisso, in quanto piuttosto un codice non corretto lotto dipende dal comportamento corrente utilizzando per esempio [something]* come la regex. L'uso di tali modelli sarà ora genererà un FutureWarning e quelli che mai può dividere nulla, lanciare una ValueError da Python 3.5 in poi:

>>> re.split(r'(?<!foo)(?=bar)', 'foobarbarbazbar') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.6/re.py", line 212, in split 
    return _compile(pattern, flags).split(string, maxsplit) 
ValueError: split() requires a non-empty pattern match. 

L'idea è che dopo un certo periodo di avvertimenti, il comportamento può essere modificato in modo che la tua espressione regolare funzionasse di nuovo.


Se non è possibile utilizzare il modulo regex, è possibile scrivere il proprio funzione split con re.finditer():

def megasplit(pattern, string): 
    splits = list((m.start(), m.end()) for m in re.finditer(pattern, string)) 
    starts = [0] + [i[1] for i in splits] 
    ends = [i[0] for i in splits] + [len(string)] 
    return [string[start:end] for start, end in zip(starts, ends)] 

print(megasplit(r'(?<!foo)(?=bar)', 'foobarbarbazbar')) 
print(megasplit(r'o', 'foobarbarbazbar')) 

Se si è certi che le partite sono solo larghezza zero, è possibile utilizzare l'inizio delle suddivisioni per codice più semplice:

import re 

def zerowidthsplit(pattern, string): 
    splits = list(m.start() for m in re.finditer(pattern, string)) 
    starts = [0] + splits 
    ends = splits + [ len(string) ] 
    return [string[start:end] for start, end in zip(starts, ends)] 

print(zerowidthsplit(r'(?<!foo)(?=bar)', 'foobarbarbazbar')) 
+0

Mentre il metodo 'findall' nell'altra risposta è intelligente, richiede che il pattern" foo "venga ripetuto due volte nella stessa regex. Se "foo" fosse in realtà un segnaposto per un modello molto più complicato, sarebbe del tutto indesiderabile. Questa risposta è la più scalabile e pratica per le espressioni regolari complicate e inoltre non richiede l'installazione di moduli aggiuntivi (il che elimina anche la necessità di rifattorizzare il codice esistente per lavorare con 'regex'), ed è per questo che sono accettando questa come la migliore risposta. – Shashank

+0

@Shashank ha aggiunto una funzione di suddivisione che funziona correttamente con corrispondenze di larghezza zero e larghezza non pari a –

+0

In che modo il codice errato può fare affidamento su qualcosa che non è implementato? Ci sono pochissime aree per le quali Python oggettivamente fa schifo, e questo bellissimo esempio. –

3
import regex 
x="bazbarbarfoobar" 
print regex.split(r"(?<!baz)(?=bar)",x,flags=regex.VERSION1) 

Qui è possibile utilizzare il modulo regex.

o

(.+?(?<!foo))(?=bar|$)|(.+?foo)$ 

Usa re.findall.

See demo

+0

Intendi il modulo su PyPI che dovrebbe sostituire 're' in futuro? – Shashank

+0

Ho dovuto Google perché la tua risposta non aveva un link. : p Ma questo è bello sapere. Qualche idea quando è prevista la sostituzione? – Shashank

+0

La soluzione 'findall' è intelligente. :) Ma ho trovato un bug con questo: 're.findall (r '(. +? (? Shashank

Problemi correlati