Regex dokładnie n OR m razy

105

Rozważmy następujące wyrażenie regularne, gdzie Xjest dowolne wyrażenie regularne.

X{n}|X{m}

To wyrażenie regularne sprawdzałoby , czy Xwystępuje dokładnie n lub mrazy.

Czy istnieje kwantyfikator wyrażeń regularnych, który może Xdokładnie sprawdzać wystąpienie nlub mczas?

FThompson
źródło
Numer Dwa wystąpień Xjest najlepszy można dostać za ogólny m, n.
John Dvorak,
Gdyby to był mój problem, wypróbowałbym odwołanie wsteczne regex i zacząłbym od (X)\1{n-1}(?:\1{m-n-1}). Wiem, że to pasuje Xprzynajmniej raz, ale żeby zacząć, wypróbuj tę prostą rzecz, a następnie udoskonal, używając zamiast tego lookaheads lub lookbinds (X).
nalply

Odpowiedzi:

91

Nie ma jednego kwantyfikatora, który oznacza „dokładnie m lub n razy”. Sposób, w jaki to robisz, jest w porządku.

Alternatywą jest:

X{m}(X{k})?

gdzie m < ni kjest wartością n-m.

Mark Byers
źródło
67

Oto pełna lista kwantyfikatorów (patrz http://www.regular-expressions.info/reference.html ):

  • ?, ??- 0 lub 1 wystąpień ( ??jest leniwy, ?jest chciwy)
  • *, *?- dowolna liczba wystąpień
  • +, +?- co najmniej jedno wystąpienie
  • {n}- dokładnie noccurences
  • {n,m}- ndo mzdarzeń włącznie
  • {n,m}?- nna mwypadki, leniwy
  • {n,}, {n,}?- co najmniej nwystępowania

Aby uzyskać „dokładnie N lub M”, musisz dwukrotnie napisać wyrażone ilościowo wyrażenie regularne, chyba że m, n są specjalne:

  • X{n,m} Jeśli m = n+1
  • (?:X{n}){1,2} Jeśli m = 2n
  • ...
John Dvorak
źródło
1
Dlaczego jest to ?:potrzebne w m = 2nprzykładzie if ? Wydaje mi się, że bez niego działa dobrze.
erb
7
@erb jeśli je opuścisz ?:, grupa stanie się grupą przechwytującą. Oprócz tego, że silnik regex zapamiętuje rzeczy, których nie musi, jeśli masz grupy przechwytujące po tej, ich identyfikatory ulegną zmianie. Jeśli używasz wyrażenia regularnego do zamiany, będziesz musiał dostosować zamianę.
John Dvorak
3

TLDR; (?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

Wygląda na to, że chcesz „xn razy” lub „xm razy”, myślę, że dosłowne tłumaczenie wyrażenia regularnego wyglądałoby (x{n}|x{m}). tak: https://regex101.com/r/vH7yL5/1

lub w przypadku, gdy możesz mieć sekwencję większą niż m „x” s (zakładając m> n), możesz dodać „po braku” x ”i„ po którym nie ma „x”, co oznacza [^x](x{n}|x{m})[^x], że załóż, że za tobą i za tobą zawsze jest znak "x". Jak widać tutaj: https://regex101.com/r/bB2vH2/1

możesz to zmienić na (?:[^x]|^)(x{n}|x{m})(?:[^x]|$), tłumacząc na „następujący po braku 'x' lub następujący po początku linii” i „po którym następuje brak 'x' lub po którym następuje koniec linii”. Ale nadal nie będzie pasował do dwóch sekwencji z tylko jednym znakiem między nimi (ponieważ pierwsze dopasowanie wymagałoby znaku po, a drugie znaku wcześniej), jak widać tutaj: https://regex101.com/r/ oC5oJ4 / 1

Na koniec, aby dopasować odległe dopasowanie o jeden znak, możesz dodać pozytywne spojrzenie w przód (? =) Na „brak 'x' po” lub pozytywne spojrzenie w tył (? <=) Na „brak 'x” przed ”, w ten sposób: https://regex101.com/r/mC4uX3/1

(?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

W ten sposób dopasujesz tylko dokładną liczbę „x”, jaką chcesz.

Wzmocniony
źródło
1

Patrząc na odpowiedź Enhardened, stwierdzają, że ich przedostatnie wyrażenie nie będzie pasować do sekwencji z tylko jednym znakiem między nimi. Istnieje łatwy sposób na naprawienie tego problemu bez użycia patrzenia w przód / w tył, a jest to zastąpienie znaku początku / końca znakiem granicy. Pozwala to dopasować do granic słów, które obejmują początek / koniec. W związku z tym odpowiednie wyrażenie powinno brzmieć:

(?:[^x]|\b)(x{n}|x{m})(?:[^x]|\b)

Jak widać tutaj: https://regex101.com/r/oC5oJ4/2 .

rozza2058
źródło
1
Fajnie, nie wiedziałem, jak regex radzi sobie z granicami. Jedynym problemem związanym z tą metodą jest użycie niestandardowej granicy. Opowieść wygląd: regex101.com/r/j0nkeo/1 i regex101.com/r/4Ix7Dr/1
Enhardened
1
@Enhardened - to dobra uwaga, wydaje się, że jest to problem z wieloma pasującymi grupami, które się nakładają. To jest sytuacja, w której musiałbyś spojrzeć za siebie.
rozza2058
1

Bardzo stary post, ale chciałbym wnieść coś, co może być pomocne. Wypróbowałem to dokładnie w sposób opisany w pytaniu i działa, ale jest haczyk: kolejność ilości ma znaczenie. Rozważ to:

#[a-f0-9]{6}|#[a-f0-9]{3}

Spowoduje to wyświetlenie wszystkich wystąpień szesnastkowych kodów kolorów (mają one długość 3 lub 6 cyfr). Ale kiedy odwracam to w ten sposób

#[a-f0-9]{3}|#[a-f0-9]{6}

znajdzie tylko 3-cyfrowe lub pierwsze 3 cyfry 6-cyfrowych. Ma to sens i profesjonalista firmy Regex może to od razu zauważyć, ale dla wielu może to być dziwne zachowanie. Istnieją pewne zaawansowane funkcje Regex, które mogą uniknąć tej pułapki niezależnie od kolejności, ale nie wszyscy są po kolana w wzorach Regex.

DanDan
źródło