2015-05-01 14 views
7

Dzięki modułowi re wydaje się, że nie jestem w stanie podzielić na mecze wzorców, które są puste ciągi:Python regex: dzielenie na wzór meczu, który jest ciągiem pustym

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

Innymi słowy, nawet jeśli dopasowanie zostanie znalezione, jeśli jest to pusty ciąg, nawet re.split nie może podzielić ciągu znaków.

The docs for re.split wydaje się wspierać moje wyniki.

A „obejście” było dość łatwe do znalezienia w tym konkretnym przypadku:

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

Ale jest to sposób podatne na błędy to zrobić, bo wtedy mam uważać na ciągi, które już zawierają podciąg że jestem podział na:

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

Czy istnieje lepszy sposób podzielić na pusty meczu wzór z modułem re? Ponadto, dlaczego re.split nie pozwala mi to zrobić w pierwszej kolejności? Wiem, że jest to możliwe z innymi algorytmami podziału, które działają z regex; na przykład mogę to zrobić za pomocą wbudowanego JavaScriptu String.prototype.split().

Odpowiedz

8

Szkoda, że ​​split wymaga niezerowej szerokość mecz, ale to nie było jeszcze ustalona, ​​ponieważ sporo niepoprawny kod zależy od aktualnego zachowania za pomocą na przykład [something]* jako regex. Zastosowanie takich wzorów będzie teraz generować FutureWarning a te, które nigdy można podzielić coś, rzucić ValueError z Pythonem 3.5 roku:

>>> 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. 

Pomysł jest, że po pewnym okresie ostrzeżeń, zachowanie może być zmienione aby Twój regularny wyraz zadziałał ponownie.


Jeśli nie można użyć modułu regex, można napisać własną funkcję podziału za pomocą 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')) 

Jeżeli jesteś pewien, że mecze są tylko zerowej szerokości, można użyć początki podziałów dla łatwiejszego kodu:

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

Podczas gdy metoda "findall" w drugiej odpowiedzi jest sprytna, wymaga powtórzenia schematu "foo" dwa razy w tym samym regex. Jeśli "foo" byłby faktycznie elementem zastępczym o dużo bardziej skomplikowanym wzorze, byłoby to całkowicie niepożądane. Ta odpowiedź jest najbardziej skalowalna i praktyczna dla skomplikowanych wyrażeń regularnych, a także nie wymaga instalowania żadnych dodatkowych modułów (co również eliminuje konieczność przekształcania istniejącego kodu do pracy z 'regex'), i dlatego właśnie zaakceptowanie tego jako najlepszej odpowiedzi. – Shashank

+0

@Shashank dodał funkcję podziału, która działa poprawnie przy dopasowaniu szerokości zero i szerokości niezerowej –

+0

Jak nieprawidłowy kod może polegać na czymś, co nie jest zaimplementowane? Jest bardzo mało obszarów, dla których Python obiektywnie jest do bani, i ten jeden świetny przykład. –

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

Możesz użyć tutaj modułu regex.

lub

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

Zastosowanie re.findall.

See demo

+0

Masz na myśli moduł na PyPI, który ma zastąpić 're' w przyszłości? – Shashank

+0

Musiałem to zrobić, ponieważ Twoja odpowiedź nie zawierała linku. : p Ale dobrze o tym wiedzieć. Masz pomysł, kiedy zaplanowano wymianę? – Shashank

+0

Rozwiązanie "findall" jest sprytne. :) Ale znalazłem błąd: 're.findall (r '(. +? (? Shashank