2016-08-16 44 views
5

Przede wszystkim pytanie jest oznaczone Python i regex, ale tak naprawdę nie jest z nimi związany - odpowiedź może być na wysokim poziomie.Podziel ciąg znaków przez pierwsze wystąpienie z zestawu ograniczników z python i regex

W tej chwili dzielę łańcuch z wieloma ogranicznikami na następujący wzór. Istnieje rzeczywiście bardziej wytyczenie wzory i są bardziej złożone, ale trzymajmy go prosty i ograniczyć je do 2 znaków - # i *:

parts = re.split('#|*', string)

których takie podejście ciąg aaa#bbb*ccc#ddd jest podzielony na 4 podciągi aaa, bbb , ccc, ddd. Ale wymagane jest podzielenie przez separator, który występuje jako pierwszy w łańcuchu lub przez ogranicznik, który jest najczęściej w ciągu znaków. aaa#bbb*ccc#ddd należy podzielić na aaa, bbb*ccc, ddd i aaa*bbb#ccc*ddd należy podzielić na aaa, bbb#ccc, ddd.

Znam prosty sposób, aby to osiągnąć - znaleźć, co separator występuje jako pierwszy lub jest najczęstszy w ciągu, a następnie podzielić go tym pojedynczym ogranicznikiem. Ale metoda musi być wydajna i zastanawiam się, czy można to osiągnąć za pomocą pojedynczego wyrażenia wyrażenia regularnego. Pytanie dotyczy głównie podziału z pierwszym pojawieniem się zestawu ograniczników - dla najczęstszych przypadków ograniczników prawie na pewno konieczne będzie obliczenie liczby wystąpień z góry.

Aktualizacja:

Pytanie nie prosi, aby podzielić przez pierwszego wystąpienia lub najczęstszego separatora jednocześnie - każda z tych metod indywidualnie będzie wystarczająca. Rozumiem, że dzielenie przez najczęstsze separatory nie jest możliwe w przypadku wyrażenia regularnego bez wstępnego określenia separatora, ale myślę, że jest szansa, że ​​podział po pierwszym wystąpieniu jest możliwy z regex i uprzedzeniem bez wcześniejszego przygotowania.

+4

Żaden regex nie znajdzie * najczęstszego * wzoru. Będziesz musiał polegać na innych środkach językowych. –

Odpowiedz

3

wymagane jest podzielenie przez separator, który występuje jako pierwszy w łańcuchu lub przez ogranicznik, który jest najczęściej w ciągu znaków.

więc można najpierw znaleźć wszystkie separatory i zachować je w odpowiednim pojemniku z ich częstotliwością, a następnie znaleźć najbardziej popularna i pierwszy z nich, a następnie podzielić ciąg na ich podstawie.

Teraz, aby znaleźć separatory, należy oddzielić je od zwykłego tekstu na podstawie określonej cechy, na przykład, jeśli nie są one znakami słownymi, a dla ich zachowania możemy użyć słownika w celu zachowania liczby znaków. podobne ograniczniki (w tym przypadku wykona zadanie collections.Counter()).

Demo:

>>> s = "aaa#bbb*ccc#ddd*rkfh^ndjfh*dfehb*[email protected]*rjh" 
>>> delimiters = re.findall(r'\W', s) 
>>> first = delimiters[0] 
'#' 
>>> Counter(delimiters) 
Counter({'*': 5, '#': 2, '@': 1, '-': 1, '^': 1}) 
>>> 
>>> frequent = Counter(delimiters).most_common(1)[0][0] 
'*' 
>>> re.split(r'\{}|\{}'.format(first, frequent), s) 
['aaa', 'bbb', 'ccc', 'ddd', 'rkfh^ndjfh', 'dfehb', '[email protected]', 'rjh'] 

Pamiętaj, że jeśli masz do czynienia z ogranicznikami, które mają więcej niż jeden znaków można użyć re.escape() aby uniknąć znaki specjalne regex (jak *).

+1

Ach masz rację ++ – anubhava

+0

Pytanie właściwie zadawane, aby uniknąć wstępnych kroków, aby znaleźć pierwsze i najczęstsze ograniczniki. Ale jeśli nie da się tego uniknąć, ta odpowiedź jest dla nich dobra. –

0

Odkryłem, że metoda string.count() jest bardzo szybka, ponieważ jest zaimplementowana w języku C. Wszystko, co unika pętli, będzie na ogół szybsze, nawet jeśli wielokrotnie powtarzasz ciąg znaków.Jest to prawdopodobnie najszybsze rozwiązanie:

>>> s = 'aaa*bbb#ccc*ddd' 
>>> a, b = s.count('*'), s.count('#') 
>>> if a == b: a, b = -s.find('*'), -s.find('#') 
... 
>>> s.split('*' if a > b else '#') 
['aaa', 'bbb#ccc', 'ddd'] 
+0

.count() i .find() są szybsze niż .split(), a jeśli .split() jest najszybszym sposobem podziału ... powinno to być z łatwością mniej niż 5-krotność szybkości optymalnego rozwiązania. (zgadnę mniej niż 2x) –