Wskazówki dotyczące Regex Golf


43

Podobne do naszych wątków dotyczących golfowych wskazówek: jakie są ogólne sztuczki skracania wyrażeń regularnych?

Widzę trzy zastosowania wyrażenia regularnego, jeśli chodzi o golfa: klasyczny wyrażenie regularne („tutaj jest lista, która powinna pasować, a oto lista, która powinna zawieść”), użycie wyrażenia regularnego do rozwiązywania problemów obliczeniowych i wyrażeń regularnych używanych jako części większy kod do gry w golfa. Zapraszam do publikowania wskazówek dotyczących jednego lub wszystkich z nich. Jeśli Twoja wskazówka jest ograniczona do jednego lub więcej smaków, podaj te smaki u góry.

Jak zwykle, trzymaj się jednej wskazówki (lub rodziny bardzo blisko powiązanych wskazówek) na odpowiedź, aby najbardziej przydatne wskazówki mogły awansować na szczyt poprzez głosowanie.


Rażąca autopromocja: do jakiej kategorii należy użyć wyrażenia regularnego? codegolf.stackexchange.com/a/37685/8048
Kyle Strand

@KyleStrand „wyrażenia regularne używane jako części większego kodu golfowego”.
Martin Ender

Odpowiedzi:


24

Kiedy nie uciec

Te zasady dotyczą większości smaków, jeśli nie wszystkich:

  • ] nie potrzebuje ucieczki, gdy nie ma sobie równych.

  • {i }nie trzeba uciekać, gdy nie są częścią powtórzenia, np. {a}mecze {a}dosłownie. Nawet jeśli chcesz coś dopasować {2}, musisz uciec tylko jednemu z nich, np {2\}.

W klasach postaci:

  • ]nie musi uciekać, gdy jest to pierwsza postać w zestawie znaków, np. []abc]pasuje do jednego z znaków ]abc, lub gdy jest to druga postać po ^, np. [^]]pasuje do wszystkiego oprócz ]. (Godny uwagi wyjątek: smak ECMAScript!)

  • [wcale nie potrzebuje ucieczki. W połączeniu z powyższą wskazówką oznacza to, że możesz dopasować oba nawiasy do okropnie intuicyjnej klasy postaci [][].

  • ^nie potrzebuje ucieczki, kiedy to nie pierwszy znak w zestawie znaków, np [ab^c].

  • -nie potrzebuje ucieczki, kiedy to zarówno pierwszy (drugi po ^) lub ostatni znak w zestawie znaków, na przykład [-abc], [^-abc]albo [abc-].

  • Żadne inne znaki nie muszą uciekać wewnątrz klasy znaków, nawet jeśli są meta znakami poza klasami znaków (z wyjątkiem \samego ukośnika odwrotnego ).

Ponadto, w niektórych smakach ^i $są dopasowywane dosłownie, gdy nie znajdują się odpowiednio na początku lub na końcu wyrażenia regularnego.

(Podziękowania dla @ MartinBüttner za wypełnienie kilku szczegółów)


Niektórzy wolą uciekać od rzeczywistej kropki, umieszczając ją w klasie postaci, w której nie potrzebuje ona ucieczki (np. [.]). Ucieczka to normalnie zaoszczędziłoby w tym przypadku 1 bajt\.
CSᵠ

Należy pamiętać, że [należy je zmienić w Javie. Nie jestem jednak pewien co do ICU (używane w Androidzie i iOS) lub .NET.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨d̷̰̀ĥ̷̳

18

Proste wyrażenie regularne pasujące do wszystkich drukowalnych znaków w tabeli ASCII .

[ -~]

1
czysta wspaniałość, wszystkie znaki ze standardowej klawiatury amerykańskiej! Uwaga: standardowa tabela ascii (bez rozszerzonego zakresu 127-255
CSᵠ

Często go używam, ale brakuje w nim wspólnego „zwykłego” znaku: TAB. Zakłada się, że używasz LC_ALL = "C" (lub podobny), ponieważ niektóre inne ustawienia narodowe zawiodą.
Olivier Dulac

Czy łącznik może być używany w ten sposób do określania dowolnego zakresu znaków w tabeli ASCII? Czy to działa dla wszystkich smaków wyrażeń regularnych?
Josh Withee

14

Poznaj swoje smaki regularne

Zaskakująca jest liczba osób, które uważają, że wyrażenia regularne są zasadniczo niezależne od języka. Istnieją jednak dość znaczne różnice między smakami, a szczególnie w przypadku golfa kodowego dobrze jest znać kilka z nich i ich ciekawe funkcje, dzięki czemu możesz wybrać najlepsze dla każdego zadania. Oto przegląd kilku ważnych smaków i ich odróżnienie od innych. (Ta lista nie może być naprawdę kompletna, ale daj mi znać, jeśli coś przeoczyłem.)

Perl i PCRE

Wrzucam je do jednego garnka, ponieważ nie znam się zbytnio na smaku Perla i są one w większości równoważne (w końcu PCRE jest dla wyrażeń regularnych kompatybilnych z Perlem). Główną zaletą smaku Perla jest to, że można wywoływać kod Perla z wyrażenia regularnego i podstawiania.

  • Rekurencja / podprogramy . Prawdopodobnie najważniejsza cecha golfa (która występuje tylko w kilku odmianach).
  • Wzorce warunkowe (?(group)yes|no).
  • Podpory zmiana przypadku w ciągu zastępowania z \l, \u, \Li \U.
  • PCRE zezwala na zmianę w spojrzeniach, gdzie każda alternatywa może mieć inną (ale stałą) długość. (Większość smaków, w tym Perl, wymaga spojrzenia, aby mieć ogólną stałą długość).
  • \G aby zakotwiczyć mecz do końca poprzedniego meczu.
  • \K zresetować początek meczu
  • PCRE obsługuje zarówno właściwości znaków, jak i skrypty Unicode .
  • \Q...\Eaby uniknąć dłuższych serii znaków. Przydatne, gdy próbujesz dopasować ciąg znaków, który zawiera wiele metaznaków.

.NETTO

Jest to prawdopodobnie najsilniejszy smak, z bardzo niewielkimi niedociągnięciami.

Jednym z istotnych niedociągnięć pod względem golfowym jest to, że nie obsługuje kwantyfikatorów dzierżawczych, jak niektóre inne smaki. Zamiast tego .?+będziesz musiał pisać (?>.?).

Jawa

  • Z powodu błędu (patrz Dodatek) Java obsługuje ograniczony typ look -indind o zmiennej długości: możesz szukać do samego początku łańcucha, .*od którego możesz teraz zacząć lookahead (?<=(?=lookahead).*).
  • Obsługuje łączenie i przecinanie klas postaci.
  • Ma najbardziej rozbudowane wsparcie dla Unicode, z klasami znaków dla „skryptów, bloków, kategorii i właściwości binarnych Unicode” .
  • \Q...\E jak w Perlu / PCRE.

Rubin

W najnowszych wersjach ten smak jest podobnie potężny jak PCRE, w tym obsługa wywołań podprogramów. Podobnie jak Java, obsługuje także łączenie i przecinanie klas znaków. Jedną specjalną cechą jest wbudowana klasa znaków dla cyfr szesnastkowych: \h(i negowanych \H).

Najbardziej przydatną funkcją gry w golfa jest sposób, w jaki Ruby obsługuje kwantyfikatory. Przede wszystkim można zagnieżdżać kwantyfikatory bez nawiasów. .{5,7}+działa i tak działa .{3}?. Ponadto, w przeciwieństwie do większości innych smaków, jeśli dolną granicę kwantyfikatora 0można pominąć, np. .{,5}Jest równoważna .{0,5}.

Jeśli chodzi o podprogramy, główną różnicą między podprogramami PCRE i podprogramami Ruby jest to, że składnia Ruby jest dłuższa (?n)niż bajt \g<n>, ale podprogramy Ruby mogą być używane do przechwytywania, podczas gdy PCRE resetuje przechwytywanie po zakończeniu podprogramu.

Wreszcie, Ruby ma inną semantykę dla modyfikatorów związanych z linią niż większość innych smaków. Modyfikator, który jest zwykle wywoływany mw innych smakach, jest zawsze włączony w Ruby. Tak ^i $zawsze dopasować początek i koniec linii , nie tylko na początku i na końcu łańcucha. To może zaoszczędzić bajt, jeśli trzeba to zachowanie, ale będzie cię to kosztować dodatkowe bajty, jeśli tego nie zrobisz, bo będziesz musiał wymienić ^i $z \Ai \z, odpowiednio. Oprócz tego w Ruby jest wywoływany zwykle modyfikator s(który powoduje .dopasowanie linii) m. Nie wpływa to na liczbę bajtów, ale należy o tym pamiętać, aby uniknąć pomyłek.

Pyton

Python ma solidny smak, ale nie znam żadnych szczególnie użytecznych funkcji, których nigdzie indziej nie znajdziesz.

Jednakże , istnieje alternatywa smak , który jest przeznaczony do wymiany remodułu w pewnym momencie, a która zawiera wiele ciekawych funkcji. Oprócz dodania obsługi rekurencji, zmiennych długości znaków i operatorów kombinacji klas znaków, posiada także unikalną funkcję dopasowania rozmytego . Zasadniczo możesz określić liczbę dopuszczalnych błędów (wstawień, usunięć, podstawień), a silnik da ci przybliżone dopasowania.

ECMAScript

Smak ECMAScript jest bardzo ograniczony i dlatego rzadko przydatny do gry w golfa. Jedyną rzeczą, do której się zmierza, jest negowana pusta klasa postaci, [^] która pasuje do dowolnej postaci, a także bezwarunkowo nieudana klasa pustych postaci [](w przeciwieństwie do zwykłej (?!)). Niestety, smak nie ma żadnych cech, które sprawiają, że ten ostatni jest przydatny w przypadku normalnych problemów.

Lua

Lua ma swój własny, unikalny smak, który jest dość ograniczony (np. Nie można nawet kwantyfikować grup), ale ma kilka przydatnych i interesujących funkcji.

  • Ma dużą liczbę skrótów dla wbudowanych klas znaków , w tym interpunkcję, wielkie / małe litery i cyfry szesnastkowe.
  • Dzięki %btemu obsługuje bardzo kompaktową składnię w celu dopasowania zbalansowanych ciągów. Np. %b()Dopasowuje a, (a następnie wszystko do pasującego )(poprawnie pomijając pary dopasowane wewnętrznie). (i )mogą tu być dowolne dwie postacie.

Podnieść

Smak regexowy wzmocnienia jest zasadniczo Perlem. Ma jednak kilka fajnych nowych funkcji zastępowania wyrażeń regularnych, w tym zmiany wielkości liter i warunki warunkowe . O ile mi wiadomo, ta ostatnia jest wyjątkowa dla Boost.


Zauważ, że patrzenie w przyszłość przez przebijanie przekroczy granicę związaną w patrzeniu za siebie. Testowane w Javie i PCRE.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Nie jest .?+równoważne z .*?
CalculatorFeline

@CalculatorFeline Pierwszy z nich to dzierżawczy kwantyfikator 0 lub 1 (w odmianach obsługujących kwantyfikatory dzierżawcze), drugi to kwantyfikator 0 lub więcej.
Martin Ender

@CalculatorFeline ah Rozumiem zamieszanie. Była literówka.
Martin Ender

13

Poznaj swoje klasy postaci

Większość smaków wyrażeń regularnych ma predefiniowane klasy znaków. Na przykład \ddopasowuje cyfrę dziesiętną, która jest trzy bajty krótsza niż[0-9] . Tak, mogą się nieznacznie różnić, ponieważ \dmogą również pasować do cyfr Unicode w niektórych smakach, ale w przypadku większości wyzwań nie będzie to miało znaczenia.

Oto niektóre klasy postaci występujące w większości odmian wyrażeń regularnych:

\d      Match a decimal digit character
\s      Match a whitespace character
\w      Match a word character (typically [a-zA-Z0-9_])

Ponadto mamy również:

\D \S \W

które są negowanymi wersjami powyższego.

Pamiętaj, aby sprawdzić swój smak pod kątem ewentualnych dodatkowych klas postaci. Na przykład PCRE ma \Rdla nowych linii i Lua ma nawet takie klasy, jak małe i wielkie litery.

(Podziękowania dla @HamZa i @ MartinBüttner za zwrócenie na to uwagi)


3
\Rdla nowych linii w PCRE.
HamZa

12

Nie przejmuj się grupami, które nie przechwytują (chyba że ...)

Ta wskazówka dotyczy (przynajmniej) wszystkich popularnych smaków inspirowanych Perlem.

Może to być oczywiste, ale (gdy nie gra w golfa) dobrą praktyką jest używanie grup, które nie przechwytują, (?:...)gdy tylko jest to możliwe. Te dwie dodatkowe postacie?: są jednak marnotrawstwem podczas gry w golfa, więc po prostu używaj grup przechwytywania, nawet jeśli nie zamierzasz ich odwoływać.

Jest jednak jeden (rzadki) wyjątek: jeśli zdarzy ci się 10co najmniej 3 razy utworzyć grupę odwołań wstecznych , możesz faktycznie zaoszczędzić bajty, przekształcając wcześniejszą grupę w grupę nie przechwytującą, tak że wszystkie te \10stają się \9s. (Podobne sztuczki mają zastosowanie, jeśli używasz grupy 11co najmniej 5 razy itd.)


Dlaczego 11 potrzebuje 5 razy, aby było warto, gdy 10 wymaga 3?
Nic Hartley,

1
@QPaysTaxes może używać $9zamiast $10lub $11raz zapisuje jeden bajt. Przekształcenie $10w $9jeden wymaga ?:dwóch bajtów, więc będziesz potrzebować trzech $10sekund, aby coś zapisać. Przekształcenie $11w $9wymaga dwóch ?:s, czyli czterech bajtów, więc potrzebujesz pięciu $11s, aby coś zapisać (lub pięć $10i $11łącznie).
Martin Ender

10

Rekurencja do ponownego użycia wzoru

Garść smaków wspiera rekursję ( według mojej wiedzy , Perl, PCRE i Ruby). Nawet jeśli nie próbujesz rozwiązać problemów rekurencyjnych, ta funkcja pozwala zaoszczędzić wiele bajtów w bardziej skomplikowanych wzorach. Nie ma potrzeby nawiązywania połączenia z inną grupą (nazwaną lub numerowaną) w obrębie samej grupy. Jeśli masz pewien wzorzec, który pojawia się kilka razy w wyrażeniu regularnym, po prostu zgrupuj go i odnieś się do niego poza tą grupą. Nie różni się to od wywołania podprogramu w normalnych językach programowania. Więc zamiast

...someComplexPatternHere...someComplexPatternHere...someComplexPatternHere... 

w Perl / PCRE możesz zrobić:

...(someComplexPatternHere)...(?1)...(?1)...

lub w Ruby:

...(someComplexPatternHere)...\g<1>...\g<1>...

pod warunkiem, że jest to pierwsza grupa (oczywiście możesz użyć dowolnego numeru w połączeniu rekurencyjnym).

Zauważ, że to nie to samo, co odwołanie wsteczne ( \1). Odwołania wsteczne pasują dokładnie do tego samego ciągu, który grupa dopasowała ostatnim razem. Te wywołania podprogramów faktycznie ponownie oceniają wzorzec. Jako przykład someComplexPatternHereweźmy długą klasę znaków:

a[0_B!$]b[0_B!$]c[0_B!$]d

To by pasowało do czegoś podobnego

aBb0c!d

Należy pamiętać, że nie można tutaj używać odwołań wstecznych, zachowując zachowanie. Wsteczne nie powiedzie się na powyższy napis, bo Bi 0i !nie są takie same. Jednak w przypadku wywołań podprogramów wzorzec jest w rzeczywistości ponownie oceniany. Powyższy wzór jest całkowicie równoważny z

a([0_B!$])b(?1)c(?1)d

Przechwytywanie wywołań podprogramów

Jedna uwaga dla Perla i PCRE: jeśli grupa 1w powyższych przykładach zawiera dalsze grupy, to wywołania podprogramów nie zapamiętają ich przechwytywania. Rozważ ten przykład:

(\w(\d):)\2 (?1)\2 (?1)\2

To nie będzie pasować

x1:1 y2:2 z3:3

ponieważ po powrocie wywołań podprogramu nowe przechwytywanie grupy 2jest odrzucane. Zamiast tego ten wzór pasowałby do tego ciągu:

x1:1 y2:1 z3:1

Różni się to od Ruby, gdzie rozmowy podprogramów zrobić zachowują swoje zrzuty, więc odpowiednik Ruby regex (\w(\d):)\2 \g<1>\2 \g<1>\2będzie pasować do pierwszego z powyższych przykładach.


Możesz użyć \1dla Javascript. I PHP też (tak myślę).
Ismael Miguel

5
@IsmaelMiguel To nie jest odniesienie wsteczne. To faktycznie ocenia wzór ponownie. Na przykład (..)\1pasowałby, ababale zawodził, podczas abbagdy (..)(?1)pasowałby do tego drugiego. W rzeczywistości jest to wywołanie podprogramu w tym sensie, że wyrażenie jest stosowane ponownie, zamiast dosłownie dopasować to, co pasowało ostatnim razem.
Martin Ender

Wow, nie miałem pojęcia! Uczę się czegoś nowego każdego dnia
Ismael Miguel

W .NET (lub innych smakach bez tej funkcji):(?=a.b.c)(.[0_B!$]){3}d
jimmy23013

@ user23013, który wydaje się bardzo specyficzny dla tego konkretnego przykładu. Nie jestem pewien, czy ma to zastosowanie, jeśli ponownie wykorzystam pewien subpattern w różnych okolicznościach.
Martin Ender

9

Powodowanie niepowodzenia meczu

Kiedy używasz wyrażenia regularnego do rozwiązywania problemów obliczeniowych lub dopasowywania wysoce nieregularnych języków, czasami konieczne jest spowodowanie awarii gałęzi wzorca, niezależnie od tego, gdzie jesteś w ciągu. Naiwnym podejściem jest stosowanie pustego negatywnego spojrzenia w przyszłość:

(?!)

Zawartość (pusty wzorzec) zawsze pasuje, więc negatywne spojrzenie w przód zawsze zawodzi. Najczęściej jednak jest o wiele prostsza opcja: wystarczy użyć znaku, o którym wiesz, że nigdy nie pojawi się na wejściu. Na przykład, jeśli wiesz, że dane wejściowe zawsze będą składały się wyłącznie z cyfr, możesz po prostu użyć

!

lub jakikolwiek inny nie cyfrowy, niemetatyczny znak powodujący awarię.

Nawet jeśli twój wkład może potencjalnie zawierać jakiekolwiek podciągi, istnieją krótsze sposoby niż (?!). Dowolny smak, który pozwala na pojawienie się zakotwiczeń we wzorcu w przeciwieństwie do końca, może użyć jednego z następujących 2-znakowych rozwiązań:

a^
$a

Zauważ jednak, że niektóre smaki będą traktowane ^i $jako dosłowne znaki w tych pozycjach, ponieważ oczywiście nie mają sensu jako kotwice.

W smaku ECMAScript istnieje również dość eleganckie rozwiązanie 2-znakowe

[]

Jest to pusta klasa postaci, która stara się upewnić, że następne znaki są jedną z tych w klasie - ale w klasie nie ma żadnych znaków, więc zawsze się to nie powiedzie. Zauważ, że to nie zadziała w żadnym innym smaku, ponieważ klasy postaci zwykle nie mogą być puste.


8

Zoptymalizuj swoje OR

Ilekroć masz 3 lub więcej alternatyw w swoim RegEx:

/aliceblue|antiquewhite|aquamarine|azure/

Sprawdź, czy nie ma wspólnego początku:

/a(liceblue|ntiquewhite|quamarine|zure)/

A może nawet wspólne zakończenie?

/a(liceblu|ntiquewhit|quamarin|zur)e/

Uwaga: 3 to dopiero początek i odpowiada tej samej długości, 4+ zrobiłoby różnicę


Ale co, jeśli nie wszystkie mają wspólny przedrostek? (białe znaki dodano tylko dla przejrzystości)

/aliceblue|antiquewhite|aqua|aquamarine|azure
|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood
|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan/

Pogrupuj je, dopóki zasada 3+ ma sens:

/a(liceblue|ntiquewhite|qua|quamarine|zure)
|b(eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood)
|c(adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

Lub nawet uogólnij, jeśli entropia spełnia twój przypadek użycia:

/\w(liceblue|ntiquewhite|qua|quamarine|zure
|eige|isque|lack|lanchedalmond|lue|lueviolet|rown|urlywood
|adetblue|hartreuse|hocolate|oral|ornflowerblue|ornsilk|rimson|yan)/

^ w tym przypadku jesteśmy pewni, że nie otrzymamy żadnego cluelubcrown slack Ryan

To „według niektórych testów” również poprawia wydajność, ponieważ zapewnia kotwicę na początek.


1
Jeśli wspólny początek lub koniec jest dłuższy niż jeden znak, nawet zgrupowanie dwóch może mieć znaczenie. Jak aqua|aquamarineaqua(|marine)lub aqua(marine)?.
Paŭlo Ebermann

6

Ten jest dość prosty, ale warto powiedzieć:

Jeśli zauważysz, że powtarzasz klasę postaci [a-zA-Z], prawdopodobnie możesz po prostu użyć [a-z]i dołączyć i(case- i nsensitive modyfikator) do wyrażenia regularnego.

Na przykład w Ruby następujące dwa wyrażenia regularne są równoważne:

/[a-zA-Z]+\d{3}[a-zA-Z]+/
/[a-z]+\d{3}[a-z]/i - 7 bajtów krótszych

W tym przypadku inne modyfikatory mogą również skrócić całkowitą długość. Zamiast tego:

/(.|\n)/

który pasuje do DOWOLNEGO znaku (ponieważ kropka nie pasuje do nowej linii), użyj s Ingle-line modyfikatora s, co sprawia dot nowe linie meczu.

/./s - 3 bajty krótsze


W Ruby istnieje mnóstwo wbudowanych klas postaci dla wyrażeń regularnych. Zobacz tę stronę i wyszukaj „Właściwości postaci”.
Doskonałym przykładem jest „Symbol waluty”. Według Wikipedii istnieje mnóstwo możliwych symboli walutowych, a umieszczenie ich w klasie postaci byłoby bardzo drogie ( [$฿¢₡Ð₫€.....]), podczas gdy można dopasować dowolny z nich w 6 bajtach:\p{Sc}


1
Z wyjątkiem JavaScript, gdzie smodyfikator nie jest obsługiwany. :( Ale tam możesz użyć zastrzeżonej /[^]/sztuczki JavaScript .
Manatwork

Zauważ, że (.|\n)nawet nie działa w niektórych smakach, ponieważ .często również nie pasuje do innych typów separatorów linii. Jednak zwyczajowym sposobem wykonania tego (bez s) [\s\S]są te same bajty, co (.|\n).
Martin Ender

@ MartinBüttner, moim pomysłem było zachowanie go razem z innymi wskazówkami dotyczącymi zakończenia linii. Ale jeśli uważasz, że ta odpowiedź dotyczy bardziej modyfikatorów, nie mam zastrzeżeń, jeśli ją ponownie opublikujesz.
manatwork

Wykonano @manatwork (i dodano także inną sztuczkę niezwiązaną z ES)
Martin Ender

6

Prosty parser języka

Możesz zbudować bardzo prosty parser z podobnym do RE \d+|\w+|".*?"|\n|\S. Żetony, które musisz dopasować, są oddzielone znakiem RE ”lub„.

Za każdym razem, gdy silnik RE próbuje dopasować się do bieżącej pozycji w tekście, wypróbuje pierwszy wzorzec, a następnie drugi itd. Jeśli to się nie powiedzie (na przykład tutaj spacja), przechodzi dalej i ponownie próbuje dopasować . Porządek jest ważny. Jeśli umieściliśmy \Stermin przed \d+terminem,\S pasowałby on pierwszy do dowolnej spacji, która złamałaby nasz parser.

Moduł ".*?"dopasowywania ciągów używa nie chciwego modyfikatora, więc dopasowujemy tylko jeden ciąg naraz. Jeśli twój RE nie ma niechcianych funkcji, możesz użyć "[^"]*"ekwiwalentu.

Przykład Python:

text = 'd="dogfinder"\nx=sum(ord(c)*872 for c in "fish"+d[3:])'
pat = r'\d+|\w+|".*?"|\n|\S'
print re.findall(pat, text)

['d', '=', '"dogfinder"', '\n', 'x', '=', 'sum', '(', 'ord', '(', 'c', ')',
    '*', '872', 'for', 'c', 'in', '"fish"', '+', 'd', '[', '3', ':', ']', ')']

Przykład gry w golfa w Pythonie:

# assume we have language text in A, and a token processing function P
map(P,findall(r'\d+|\w+|".*?"|\n|\S',A))

Możesz dostosować wzory i ich kolejność do języka, który chcesz dopasować. Ta technika działa dobrze dla JSON, podstawowego HTML i wyrażeń liczbowych. Był z powodzeniem używany wiele razy w Pythonie 2, ale powinien być na tyle ogólny, aby działać w innych środowiskach.


6

\K zamiast pozytywnego wyglądu

PCRE i Perl obsługują sekwencję zmiany znaczenia \K, która resetuje początek dopasowania. Oznacza to, ab\Kcdże Twój ciąg wejściowy musi zawierać, abcdale zgłoszone dopasowanie będzie tylko cd.

Jeśli używasz pozytywnego wyglądu za początkiem wzoru (co jest prawdopodobnie najbardziej prawdopodobnym miejscem), to w większości przypadków możesz \Kzamiast tego użyć i zapisać 3 bajty:

(?<=abc)def
abc\Kdef

Jest to równoważne w większości celów, ale nie do końca. Różnice niosą ze sobą zarówno zalety, jak i wady:

  • Zaleta: PCRE i Perl nie obsługują spojrzeń o dowolnej długości (tylko .NET obsługuje). Oznacza to, że nie możesz zrobić czegoś takiego (?<=ab*). Ale dzięki \Kniemu możesz postawić przed nim dowolny wzór! Tak ab*\Kdziała To sprawia, że ​​ta technika jest znacznie potężniejsza w przypadkach, w których ma zastosowanie.
  • Zaleta: Lookarounds nie cofają się. Jest to istotne, jeśli chcesz uchwycić coś w wyglądzie, aby odwołać się później, ale istnieje kilka możliwych przechwyceń, które prowadzą do prawidłowych dopasowań. W takim przypadku silnik regex wypróbowałby tylko jedną z tych możliwości. Podczas korzystania z \Ktej części wyrażenia regularnego jest cofane, podobnie jak wszystko inne.
  • Wada: jak zapewne wiesz, kilka dopasowań wyrażenia regularnego nie może się pokrywać. Często do obejścia tego ograniczenia używa się lookaroundów, ponieważ lookahead może sprawdzić poprawność części ciągu, która została już zużyta przez wcześniejsze dopasowanie. Więc jeśli chcesz dopasować wszystkie znaki, które nastąpiły, ab możesz użyć (?<=ab).. Biorąc pod uwagę wkład

    ababc
    

    to pasowałoby do drugiego ai c. Nie można tego odtworzyć za pomocą \K. Jeśli użyjesz ab\K., dostaniesz tylko pierwsze dopasowanie, ponieważ teraz abnie ma na to spojrzenia.


Jeśli wzorzec używa \Ksekwencji ucieczki w ramach asercji dodatniej, zgłoszony początek udanego dopasowania może być dłuższy niż koniec dopasowania.
hwnd

@hwnd Chodzi mi o to, że biorąc pod uwagę ababc, nie ma sposobu, aby dopasować zarówno drugi ai tym cz \K. Dostaniesz tylko jeden mecz.
Martin Ender

Masz rację, nie z samą funkcją. Musiałbyś zakotwiczyć\G
hwnd

@ hwnd Ah Teraz rozumiem twój punkt widzenia. Ale wydaje mi się, że w tym momencie (z perspektywy golfa) lepiej jest mieć negatywne spojrzenie, ponieważ może i tak możesz go potrzebować, ponieważ nie możesz być pewien, że .od ostatniego meczu był naprawdę a.
Martin Ender,

1
Ciekawe wykorzystanie \ K =)
hwnd

5

Dopasowanie dowolnej postaci

W smaku ECMAScript brakuje smodyfikatorów, które .pasują do dowolnego znaku (w tym nowego wiersza). Oznacza to, że nie ma jednoznakowego rozwiązania pozwalającego na dopasowanie całkowicie dowolnych znaków. Standardowe rozwiązanie w innych smakach (gdy sz jakiegoś powodu nie chce się używać ) to [\s\S]. Jednak ECMAScript jest tylko smak (według mojej wiedzy), które obsługuje pustych klas postaci, a więc ma znacznie krótszy alternatywy: [^]. Jest to zanegowana pusta klasa postaci - oznacza to, że pasuje do dowolnej postaci.

Nawet w przypadku innych smaków możemy nauczyć się z tej techniki: jeśli nie chcemy używać s(np. Ponieważ nadal musimy mieć zwykłe znaczenie .w innych miejscach), nadal może istnieć krótszy sposób dopasowania zarówno znaków nowej linii, jak i znaków do wydruku, pod warunkiem, że jest jakiś znak, o którym wiemy, że nie pojawia się na wejściu. Powiedzmy, że przetwarzamy liczby rozdzielane znakami nowej linii. Następnie możemy dopasować dowolny znak [^!], ponieważ wiemy, że !nigdy nie będzie on częścią ciągu. To oszczędza dwa bajty nad naiwnym [\s\S]lub [\d\n].


4
W Perlu \Noznacza dokładnie to, co .oznacza poza /strybem, z wyjątkiem tego, że nie ma na niego wpływu tryb.
Konrad Borowski,

4

Używaj grup atomowych i kwantyfikatorów dzierżawczych

Stwierdzono, że grupy (atomowych (?>...)) i zaborczy kwantyfikatorów ( ?+, *+, ++,{m,n}+ ) często bardzo użyteczne do golfa. Dopasowuje ciąg i nie pozwala na późniejsze śledzenie. Będzie więc pasował tylko do pierwszego pasującego łańcucha, który zostanie znaleziony przez silnik regex.

Na przykład: Aby dopasować ciąg o nieparzystej liczbie ana początku, po którym nie występują kolejne a, możesz użyć:

^(aa)*+a
^(?>(aa)*)a

To pozwala ci używać takich rzeczy jak .* swobodne , a jeśli istnieje oczywiste dopasowanie, nie będzie innej możliwości dopasowania zbyt dużej lub zbyt małej liczby znaków, co może zepsuć twój wzór.

W wyrażeniu regularnym .NET (które nie ma kwantyfikatorów dzierżawczych) możesz użyć tego, aby wyskoczyć w grupie 1 największą wielokrotność 3 (maksymalnie 30) razy (niezbyt dobrze golfa):

(?>((?<-1>){3}|){10})

1
W skrypcie ECMA brakuje również kwantyfikatorów dzierżawczych lub grup atomowych :(
CSᵠ

4

Zapomnij o przechwyconej grupie po podwyrażeniu (PCRE)

Dla tego wyrażenia regularnego:

^((a)(?=\2))(?!\2)

Jeśli chcesz wyczyścić \ 2 po grupie 1, możesz użyć rekurencji:

^((a)(?=\2)){0}(?1)(?!\2)

Będzie pasował, aapodczas gdy poprzedni nie. Czasami możesz również użyć ??lub nawet ?zamiast {0}.

Może to być przydatne, jeśli często używałeś rekurencji, a niektóre odnośniki zwrotne lub grupy warunkowe pojawiły się w różnych miejscach wyrażenia regularnego.

Należy również pamiętać, że grupy atomowe są zakładane dla rekurencji w PCRE. Więc to nie pasuje do jednej litery a:

^(a?){0}(?1)a

Nie próbowałem tego jeszcze w innych smakach.

W przypadku lookaheads możesz w tym celu użyć podwójnych negatywów:

^(?!(?!(a)(?=\1))).(?!\1)

4

Wyrażenia opcjonalne

Czasami warto o tym pamiętać

(abc)?

jest w większości taki sam jak

(abc|)

Jest jednak niewielka różnica: w pierwszym przypadku grupa albo przechwytuje, abcalbo wcale nie przechwytuje. Ten drugi przypadek spowodowałby bezwarunkowe niepowodzenie odniesienia wstecznego. W drugim wyrażeniu grupa będzie przechwytywać abclub pusty ciąg znaków, przy czym w drugim przypadku bezwarunkowo dopasuje się odwołanie wsteczne . Aby naśladować to drugie zachowanie ?, musisz otoczyć wszystko inną grupą, co kosztowałoby dwa bajty:

((abc)?)

Używana wersja |jest również przydatna, gdy chcesz owinąć wyrażenie w inną formę grupy i nie przejmuj się przechwytywaniem:

(?=(abc)?)
(?=abc|)

(?>(abc)?)
(?>abc|)

Wreszcie, ta sztuczka może być również zastosowana do niestrawności, ?gdzie oszczędza bajt nawet w jego surowej postaci (a w konsekwencji 3 bajty w połączeniu z innymi formami grup):

(abc)??
(|abc)

1

Wiele wyprzedzających, które zawsze pasują (.NET)

Jeśli masz co najmniej 3 konstrukcje lookahead, które zawsze pasują (do przechwytywania podwyrażeń), lub jeśli na lookahead znajduje się kwantyfikator, po którym następuje coś innego, więc powinny znajdować się w niekoniecznie przechwyconej grupie:

(?=a)(?=b)(?=c)
((?=a)b){...}

Są one krótsze:

(?(?(?(a)b)c))
(?(a)b){...}

gdzie anie powinna być nazwa przechwyconej grupy. Nie możesz używać tego, |by oznaczać to, co zwykle bi cbez dodawania kolejnej pary nawiasów.

Niestety równoważenie grup w warunkach warunkowych wydawało się błędne, przez co w wielu przypadkach było bezużyteczne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.