2016-09-03 21 views

Odpowiedz

21

Tak, jest to możliwe przy użyciu silnika regex że obsługuje backreferences i warunki.

Pierwsza lista kolejnych numerów może być rozłożona na liście, gdzie każda para numerów jest rzędu:

(?=(?&cons))\d+ 
(?:,(?=(?&cons))\d+)* 
,\d+ 

tutaj (?=(?&cons)) jest zastępczy predykatu, który zapewnia, że ​​dwie liczby są sobie. To orzeczenie może wyglądać następująco:

(?<cons>\b(?: 
    (?<x>\d*) 
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
       |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 
    (?:9(?= 9*,\g{x}\d (?<y>\g{y}?+ 0)))* 
    ,\g{x} 
    (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
    (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 
    (?(y)\g{y}) 
    # handle the 999 => 1000 case separately 
    | (?:9(?= 9*,1 (?<z>\g{z}?+ 0)))+ 
    ,1\g{z} 
)\b) 

Na krótkim wyjaśnieniem, drugi obsługę 999,1000 pary typu sprawa jest łatwiejsza do zrozumienia - jest bardzo szczegółowy opis, jak to działa w this answer concerned with matching a^n b^n. Połączenie między nimi polega na tym, że w tym przypadku musimy dopasować 9^n ,1 0^n.

Pierwszy przypadek jest nieco bardziej skomplikowany. Największą część z nich obsługuje prosty przypadek zwiększając cyfrę dziesiętną, która jest stosunkowo gadatliwy ze względu na liczbę tych cyfr:

(?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
      |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 

(?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
(?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 

Pierwszy blok będzie uchwycić czy cyfra jest N w grupie AN i sekundę blok użyje wtedy warunków warunkowych do sprawdzenia, która z tych grup była użyta. Jeśli grupa aN nie jest pusta, następna cyfra powinna wynosić N + 1.

Pozostała część pierwszego przypadku obsługuje takie przypadki, jak 1999,2000. To znowu wpada w schemat N 9^n, N+1 0^n, więc jest to kombinacja metody dopasowywania a^n b^n i inkrementacji cyfry dziesiętnej. Prosty przypadek 1,2 jest traktowany jako przypadek ograniczający, w którym n = 0.

pełna regex: https://regex101.com/r/zG4zV0/1


Alternatywnie (?&cons) orzeczenie można zrealizować nieco bezpośrednio, jeśli są obsługiwane rekurencyjne odniesienia podciąg wzorca:

(?<cons>\b(?: 
    (?<x>\d*) 
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
       |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 
    (?<y> 
     ,\g{x} 
     (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
     (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 
     | 9 (?&y) 0 
    ) 
    # handle the 999 => 1000 case separately 
    | (?<z> 9,10 | 9(?&z)0) 
)\b) 

W tym przypadku dwa gramatyki 9^n ,1 0^n n> = 1 i prefix N 9^n , prefix N+1 0^n, n> = 0 są właściwie napisane wprost.

Kompletne alternatywne wyrażenie: https://regex101.com/r/zG4zV0/3

+0

Nice post. Mam ogólne pytanie dotyczące _nested back reference_ To wyrażenie w twoim regex na przykład '(? \ g {y}? + 0)'. Używam silnika _Boost regex_, który jest w większości kompatybilny z PCRE i Perl. Czasami muszę stosować obejścia w określonych sytuacjach. Jednym z nich jest na przykład '(? (? & _ Y)? + 0) (? (DEFINE) (?<_y> \ g {y})) 'Napędza mnie czasem komunikat' undefined backref', że jeśli możliwe jest obejście tego problemu, czemu nie wspierać go w całości. Zastanawiasz się, czy znasz lepsze obejście, które nie wymaga wywołania funkcji? (_workaround https://regex101.com/r/zG4zV0/2_). – sln

+0

@ sln Niestety, nie znasz mechanizmu doładowania regex. Jeśli dobrze rozumiem, problem polega na tym, że funkcja boost nie obsługuje funkcji backrefs dla grup, które nie zostały jeszcze w pełni dopasowane, prawda? W takim przypadku Twoje obejście wydaje się rozsądne. Dodałem także alternatywne rozwiązanie oparte na rekursywnych odnośnikach podrzędnych, może to działa po wyjęciu z pudełka ... – NikiC

+0

To jest czas kompilacji podczas parsowania: 'grupy, które jeszcze nie zostały (w pełni) przeanalizowane. ' Tak więc odniesienie do jego macierzystej grupy przechwytywania. Rozwiązałem problem, dopuszczając backrefs do tego znaku w chwili otwierania grupy, ale przed dodaniem stanów wewnętrznych za pomocą analizy rekurencyjnej. Dawniej, pozwalało to tylko na backrefs do tego znaku, gdy parsowanie ')' zostało sparsowane. Jest to prosta maska ​​bitowa 'm_backrefs | = 1 << (markid-1)', jeśli nie zostanie znaleziony margines zamykający, generowany jest błąd, a mimo to całość się rozwija. Działa zgodnie z oczekiwaniami. Dodaję to do innych modów, które zrobiłem. – sln