To pytanie jest bardzo słabe, ponieważ nie jest jasne, o co je zadaje. Wszystkie odpowiedzi interpretowały to inaczej. @DaveF czy możesz wyjaśnić pytanie?
Nie zgadzam się, że wyrażenia regularne są niewłaściwym narzędziem do tego z kilku powodów. 1) Większość implementacji wyrażeń regularnych ma wykonalne, jeśli nie idealne rozwiązanie. 2) Często próbujesz znaleźć zrównoważone pary ograniczników w kontekście, w którym obowiązują również inne kryteria dobrze dopasowane do wyrażeń regularnych. 3) Często przekazujesz wyrażenie regularne do jakiegoś API, który akceptuje tylko wyrażenia regularne i nie masz wyboru.
Regex to PRAWE narzędzie dla zadania. Ta odpowiedź jest nieprawidłowa. Zobacz odpowiedź rogal111.
Andrew,
4
Absolutnie zgadzam się z odpowiedzią. Chociaż istnieją pewne implementacje rekurencji w wyrażeniu regularnym, są one równe maszynom o skończonym stanie i nie są przeznaczone do pracy ze strukturami zagnieżdżonymi, ale robią to Gramatyki wolne od kontekstu. Spójrz na hierarcyzm formalnych gramatyki Homsky'ego.
Nick Roz
138
Chcę dodać tę odpowiedź w celu szybkiego odwołania. Aktualizuj.
Gdy powtarzasz grupę z kwantyfikatorem dzierżawczym, nie ma sensu uczynić tej grupy atomową, ponieważ wszystkie pozycje cofania w tej grupie są usuwane przy każdym powtórzeniu. Więc pisanie (?>[^)(]+|(?R))*+jest takie samo jak pisanie (?:[^)(]+|(?R))*+. To samo dla następnego wzoru. W przypadku wersji rozwiniętej możesz tutaj umieścić kwantyfikator dzierżawczy: [^)(]*+aby zapobiec cofaniu się (w przypadku braku klamry zamykającej).
Casimir et Hippolyte
Jeśli chodzi o wzorzec Ruby 1.9, zamiast przekształcić grupę atomową w powtarzaną (która ma ograniczony interes, gdy istnieje wiele zagnieżdżonych nawiasów (...(..)..(..)..(..)..(..)..)) w ciągu tematu), możesz użyć prostej, nieprzechwycącej grupy i zamknąć wszystko w grupie atomowej: (?>(?:[^)(]+|\g<1>)*)( zachowuje się dokładnie jak kwantyfikator dzierżawczy). W Ruby 2.x dostępny jest kwantyfikator dzierżawczy.
Casimir et Hippolyte
@CasimiretHippolyte Dziękujemy! I dostosowane wzory pcre i Ruby 1.9, masz na myśli cały wzór, aby być jak ten ? Zaktualizuj się sam. Rozumiem, co masz na myśli, ale nie jestem pewien, czy nastąpiła znaczna poprawa.
Przykład byłby bardzo przydatny tutaj, nie mogę sprawić, żeby działał w przypadku takich rzeczy jak „(1, (2, 3)) (4, 5)”.
Andy Hayden
4
@AndyHayden dzieje się tak, ponieważ „(1, (2, 3))) (4, 5)” ma dwie grupy oddzielone spacją. Użyj wyrażenia regularnego z flagą globalną: / (([^ ()] | (? R)) *) / g. Oto test online: regex101.com/r/lF0fI1/1
W .NET 4.5 pojawia się następujący błąd dla tego wzoru: Unrecognized grouping construct.
nam
3
Niesamowite! To świetna funkcja wyrażeń regularnych. Dziękujemy, że jesteś jedyną osobą, która faktycznie odpowiedziała na pytanie. Również ta strona regex101 jest słodka.
Andrew
28
[^\(]*(\(.*\))[^\)]*
[^\(]*dopasowuje wszystko, co nie jest nawiasem otwierającym na początku łańcucha, (\(.*\))przechwytuje wymagany podciąg zawarty w nawiasach i [^\)]*dopasowuje wszystko, co nie jest nawiasem zamykającym na końcu łańcucha. Zauważ, że to wyrażenie nie próbuje dopasować nawiasów; bardziej odpowiedni byłby prosty parser (patrz odpowiedź Dehmanna ).
nawias wewnątrz klasy nie wymaga ucieczki. Ponieważ w środku nie jest metazakładem.
José Leal
10
Ten wyrażenie nie działa na coś takiego jak „tekst (tekst) tekst (tekst) tekst„ zwracanie ”(tekst) tekst (tekst)”. Wyrażenia regularne nie mogą liczyć nawiasów.
Christian Klauser,
17
(?<=\().*(?=\))
Jeśli chcesz zaznaczyć tekst między dwoma pasującymi nawiasami, nie masz szczęścia z wyrażeniami regularnymi. To niemożliwe (*) .
Ten wyrażenie regularne zwraca tylko tekst między pierwszym otwierającym a ostatnim nawiasem zamykającym w ciągu.
(*) Chyba że silnik regex ma takie funkcje, jak grupy równoważące lub rekurencja . Liczba silników obsługujących takie funkcje powoli rośnie, ale wciąż nie są one powszechnie dostępne.
Co oznaczają znaki „<=” i „=”? Jaki silnik regexp jest kierowany na to wyrażenie?
Christian Klauser
1
Jest to rozglądanie się, a ściślej „twierdzenia o zerowej szerokości z wyprzedzeniem / z tyłu”. Obsługuje je większość nowoczesnych silników wyrażeń regularnych.
Tomalak
Zgodnie z przykładem OP chce on uwzględnić w meczu najbardziej oddalone pareny. Wyrażenie regularne je wyrzuca.
Alan Moore
1
@Alan M: Masz rację. Ale zgodnie z tekstem pytania chce wszystkiego między skrajnymi parenami. Wybierz swój wybór. Powiedział, że próbował od wielu godzin, więc nawet nie wziął pod uwagę „wszystkiego, w tym skrajnych parens”, ponieważ jest to tak banalne: „(. *)”.
Tomalak
3
@ghayes Odpowiedź pochodzi z 2009 roku. To było dawno temu; mechanizmy wyrażeń regularnych, które pozwalają na pewną formę rekurencji, były bardziej rzadkie niż obecnie (i nadal są dość rzadkie). Wspomnę o tym w mojej odpowiedzi.
Tomalak,
14
Ta odpowiedź wyjaśnia teoretyczne ograniczenie, dlaczego wyrażenia regularne nie są odpowiednim narzędziem do tego zadania.
Wyrażenia regularne nie mogą tego zrobić.
Wyrażenia regularne oparte są na modelu obliczeniowym znanym jako Finite State Automata (FSA). Jak wskazuje nazwa, a FSAmoże zapamiętać tylko bieżący stan, nie ma informacji o poprzednich stanach.
Na powyższym schemacie S1 i S2 są dwoma stanami, w których S1 jest krokiem początkowym i końcowym. Więc jeśli spróbujemy z ciągiem 0110, przejście wygląda następująco:
0110-> S1 -> S2 -> S2 -> S2 ->S1
W powyższych krokach, gdy jesteśmy na drugim S2, czyli po parsowania 01z 0110FSA nie ma informacji o poprzednim 0w 01jak może zapamiętać tylko aktualny stan i wejście kolejny symbol.
W powyższym problemie musimy znać liczbę nawiasów otwierających; oznacza to, że należy go przechowywać w jakimś miejscu. Ale ponieważ FSAsnie można tego zrobić, nie można napisać wyrażenia regularnego.
Jednak do wykonania tego zadania można napisać algorytm. Algorytmy ogólnie podlegają Pushdown Automata (PDA). PDAjest o jeden poziom powyżej FSA. PDA ma dodatkowy stos do przechowywania dodatkowych informacji. PDA mogą być użyte do rozwiązania powyższego problemu, ponieważ możemy ' push' nawias otwierający na stosie i ' pop' je, gdy napotkamy nawias zamykający. Jeśli na końcu stos jest pusty, wówczas otwierające i zamykające pasują nawiasy. W przeciwnym razie nie.
Jest tu kilka odpowiedzi, które dowodzą, że jest to możliwe.
Jiří Herník,
1
@Marco Ta odpowiedź mówi o wyrażeniach regularnych w ujęciu teoretycznym. Wiele silników wyrażeń regularnych obecnie opiera się nie tylko na tym modelu teoretycznym i wykorzystuje dodatkową pamięć do wykonania zadania!
musibs
@ JiříHerník: nie są to wyrażenia regularne w ścisłym tego słowa znaczeniu: nie zdefiniowane przez Kleene jako wyrażenia regularne . Niektóre silniki wyrażeń regularnych rzeczywiście mają zaimplementowane dodatkowe funkcje, dzięki czemu parsują więcej niż tylko zwykłe języki .
Willem Van Onsem,
12
W rzeczywistości jest to możliwe przy użyciu wyrażeń regularnych .NET, ale nie jest to trywialne, więc przeczytaj uważnie.
Można przeczytać ciekawy artykuł tutaj . Konieczne może być również przeczytanie wyrażeń regularnych .NET. Możesz zacząć czytać tutaj .
<>Zastosowano nawiasy kątowe, ponieważ nie wymagają one ucieczki.
"""
Here is a simple python program showing how to use regular
expressions to write a paren-matching recursive parser.
This parser recognises items enclosed by parens, brackets,
braces and <> symbols, but is adaptable to any set of
open/close patterns. This is where the re package greatly
assists in parsing.
"""import re
# The pattern below recognises a sequence consisting of:# 1. Any characters not in the set of open/close strings.# 2. One of the open/close strings.# 3. The remainder of the string.# # There is no reason the opening pattern can't be the# same as the closing pattern, so quoted strings can# be included. However quotes are not ignored inside# quotes. More logic is needed for that....
pat = re.compile("""
( .*? )
( \( | \) | \[ | \] | \{ | \} | \< | \> |
\' | \" | BEGIN | END | $ )
( .* )
""", re.X)# The keys to the dictionary below are the opening strings,# and the values are the corresponding closing strings.# For example "(" is an opening string and ")" is its# closing string.
matching ={"(":")","[":"]","{":"}","<":">",'"':'"',"'":"'","BEGIN":"END"}# The procedure below matches string s and returns a# recursive list matching the nesting of the open/close# patterns in s.def matchnested(s, term=""):
lst =[]whileTrue:
m = pat.match(s)if m.group(1)!="":
lst.append(m.group(1))if m.group(2)== term:return lst, m.group(3)if m.group(2)in matching:
item, s = matchnested(m.group(3), matching[m.group(2)])
lst.append(m.group(2))
lst.append(item)
lst.append(matching[m.group(2)])else:raiseValueError("After <<%s %s>> expected %s not %s"%(lst, s, term, m.group(2)))# Unit test.if __name__ =="__main__":for s in("simple string",""" "double quote" """,""" 'single quote' ""","one'two'three'four'five'six'seven","one(two(three(four)five)six)seven","one(two(three)four)five(six(seven)eight)nine","one(two)three[four]five{six}seven<eight>nine","one(two[three{four<five>six}seven]eight)nine","oneBEGINtwo(threeBEGINfourENDfive)sixENDseven","ERROR testing ((( mismatched ))] parens"):print"\ninput", s
try:
lst, s = matchnested(s)print"output", lst
exceptValueErroras e:print str(e)print"done"
Odpowiedź zależy od tego, czy należy dopasować pasujące zestawy nawiasów, czy tylko od pierwszego otwarcia do ostatniego zamknięcia w tekście wejściowym.
Jeśli chcesz dopasować pasujące nawiasy klamrowe, potrzebujesz czegoś więcej niż wyrażeń regularnych. - patrz @dehmann
Jeśli to tylko pierwsze otwarcie do ostatniego zamknięcia, zobacz @Zach
Zdecyduj, co chcesz zrobić:
abc (123( foobar )def) xyz ) ghij
W takim przypadku musisz zdecydować, jaki kod będzie pasował.
Podczas gdy tak wiele odpowiedzi wspomina o tym w jakiejś formie, mówiąc, że regex nie obsługuje rekurencyjnego dopasowywania itd., Główny powód tego leży w korzeniach teorii obliczeń.
Język formularza {a^nb^n | n>=0} is not regular. Regex może pasować tylko do rzeczy, które stanowią część standardowego zestawu języków.
Nie użyłem wyrażenia regularnego, ponieważ trudno jest poradzić sobie z zagnieżdżonym kodem. Tak więc ten fragment kodu powinien umożliwiać przechwytywanie fragmentów kodu za pomocą zrównoważonych nawiasów:
def extract_code(data):""" returns an array of code snippets from a string (data)"""
start_pos =None
end_pos =None
count_open =0
count_close =0
code_snippets =[]for i,v in enumerate(data):if v =='{':
count_open+=1ifnot start_pos:
start_pos= i
if v=='}':
count_close +=1if count_open == count_close andnot end_pos:
end_pos = i+1if start_pos and end_pos:
code_snippets.append((start_pos,end_pos))
start_pos =None
end_pos =Nonereturn code_snippets
Użyłem tego do wyodrębnienia fragmentów kodu z pliku tekstowego.
/**
* get param content of function string.
* only params string should be provided without parentheses
* WORK even if some/all params are not set
* @return [param1, param2, param3]
*/
exports.getParamsSAFE =(str, nbParams =3)=>{const nextParamReg =/^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;constparams=[];while(str.length){// this is to avoid a BIG performance issue in javascript regexp engine
str = str.replace(nextParamReg,(full, p1)=>{params.push(p1);return'';});}returnparams;};
Nie rozwiązuje to w pełni pytania OP, ale sądzę, że niektórzy przydadzą się tutaj, aby wyszukać wyrażenie regularne struktury zagnieżdżonej.
Odpowiedzi:
Wyrażenia regularne są niewłaściwym narzędziem dla zadania, ponieważ mamy do czynienia z zagnieżdżonymi strukturami, tj. Rekurencją.
Istnieje jednak prosty algorytm do tego, który opisałem w odpowiedzi na poprzednie pytanie .
źródło
Chcę dodać tę odpowiedź w celu szybkiego odwołania. Aktualizuj.
.NET Regex przy użyciu grup bilansujących .
Gdzie
c
jest używany jako licznik głębokości.Demo na Regexstorm.com
PCRE przy użyciu wzorca rekurencyjnego .
Demo w regex101 ; Lub bez zmian:
Demo w regex101 ; Lub rozwinięty dla wydajności:
Demo w regex101 ; Wzór jest wklejony, w
(?R)
którym reprezentuje(?0)
.Perl, PHP, Notepad ++, R : perl = TRUE , pakiet Python : Regex z
(?V1)
zachowaniem Perla.Ruby używa wywołań podwyrażenia .
Z Ruby 2.0
\g<0>
można użyć do wywołania pełnego wzorca.Demo w Rubular ; Ruby 1.9 obsługuje tylko przechwytywanie rekurencji grupowej :
Demo at Rubular ( grupowanie atomowe od Ruby 1.9.3)
JavaScript API :: XRegExp.matchRecursive
Smaki JS, Java i inne wyrażenia regularne bez rekurencji do 2 poziomów zagnieżdżenia:
Demo w regex101 . Głębsze zagnieżdżenie należy dodać do wzoru.
Aby szybciej zawieść przy niezrównoważonym nawiasie, upuść
+
kwantyfikator.Java : Ciekawy pomysł z wykorzystaniem referencji od @jaytea .
Odwołanie - co oznacza to wyrażenie regularne?
źródło
(?>[^)(]+|(?R))*+
jest takie samo jak pisanie(?:[^)(]+|(?R))*+
. To samo dla następnego wzoru. W przypadku wersji rozwiniętej możesz tutaj umieścić kwantyfikator dzierżawczy:[^)(]*+
aby zapobiec cofaniu się (w przypadku braku klamry zamykającej).(...(..)..(..)..(..)..(..)..)
) w ciągu tematu), możesz użyć prostej, nieprzechwycącej grupy i zamknąć wszystko w grupie atomowej:(?>(?:[^)(]+|\g<1>)*)
( zachowuje się dokładnie jak kwantyfikator dzierżawczy). W Ruby 2.x dostępny jest kwantyfikator dzierżawczy.Możesz użyć rekursji wyrażenia regularnego :
źródło
Unrecognized grouping construct
.[^\(]*
dopasowuje wszystko, co nie jest nawiasem otwierającym na początku łańcucha,(\(.*\))
przechwytuje wymagany podciąg zawarty w nawiasach i[^\)]*
dopasowuje wszystko, co nie jest nawiasem zamykającym na końcu łańcucha. Zauważ, że to wyrażenie nie próbuje dopasować nawiasów; bardziej odpowiedni byłby prosty parser (patrz odpowiedź Dehmanna ).źródło
Jeśli chcesz zaznaczyć tekst między dwoma pasującymi nawiasami, nie masz szczęścia z wyrażeniami regularnymi. To niemożliwe (*) .
Ten wyrażenie regularne zwraca tylko tekst między pierwszym otwierającym a ostatnim nawiasem zamykającym w ciągu.
(*) Chyba że silnik regex ma takie funkcje, jak grupy równoważące lub rekurencja . Liczba silników obsługujących takie funkcje powoli rośnie, ale wciąż nie są one powszechnie dostępne.
źródło
Ta odpowiedź wyjaśnia teoretyczne ograniczenie, dlaczego wyrażenia regularne nie są odpowiednim narzędziem do tego zadania.
Wyrażenia regularne nie mogą tego zrobić.
Wyrażenia regularne oparte są na modelu obliczeniowym znanym jako
Finite State Automata (FSA)
. Jak wskazuje nazwa, aFSA
może zapamiętać tylko bieżący stan, nie ma informacji o poprzednich stanach.Na powyższym schemacie S1 i S2 są dwoma stanami, w których S1 jest krokiem początkowym i końcowym. Więc jeśli spróbujemy z ciągiem
0110
, przejście wygląda następująco:W powyższych krokach, gdy jesteśmy na drugim
S2
, czyli po parsowania01
z0110
FSA nie ma informacji o poprzednim0
w01
jak może zapamiętać tylko aktualny stan i wejście kolejny symbol.W powyższym problemie musimy znać liczbę nawiasów otwierających; oznacza to, że należy go przechowywać w jakimś miejscu. Ale ponieważ
FSAs
nie można tego zrobić, nie można napisać wyrażenia regularnego.Jednak do wykonania tego zadania można napisać algorytm. Algorytmy ogólnie podlegają
Pushdown Automata (PDA)
.PDA
jest o jeden poziom powyżejFSA
. PDA ma dodatkowy stos do przechowywania dodatkowych informacji. PDA mogą być użyte do rozwiązania powyższego problemu, ponieważ możemy 'push
' nawias otwierający na stosie i 'pop
' je, gdy napotkamy nawias zamykający. Jeśli na końcu stos jest pusty, wówczas otwierające i zamykające pasują nawiasy. W przeciwnym razie nie.źródło
W rzeczywistości jest to możliwe przy użyciu wyrażeń regularnych .NET, ale nie jest to trywialne, więc przeczytaj uważnie.
Można przeczytać ciekawy artykuł tutaj . Konieczne może być również przeczytanie wyrażeń regularnych .NET. Możesz zacząć czytać tutaj .
<>
Zastosowano nawiasy kątowe, ponieważ nie wymagają one ucieczki.Wyrażenie regularne wygląda następująco:
źródło
To jest ostateczne wyrażenie regularne:
Przykład:
zwróć uwagę, że
'(pip'
jest poprawnie zarządzany jako ciąg. (wypróbowano w regulatorze: http://sourceforge.net/projects/regulator/ )źródło
Napisałem małą bibliotekę JavaScript o nazwie zrównoważony, aby pomóc w tym zadaniu. Możesz to zrobić, robiąc to
Możesz nawet dokonać wymiany:
Oto bardziej złożony i interaktywny przykład JSFiddle .
źródło
Dodając do odpowiedzi bobble bubble , istnieją inne smaki wyrażeń regularnych, w których obsługiwane są konstrukcje rekurencyjne.
Lua
Użyj
%b()
(%b{}
/%b[]
dla nawiasów klamrowych / nawiasów kwadratowych):for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end
(patrz demo )Perl6 :
Nie nakładające się wielokrotne zrównoważone nawiasy pasują do:
Nałożenie wielu zrównoważonych nawiasów dopasowanych:
Zobacz demo .
re
Rozwiązanie non-regex w języku PythonZobacz odpowiedź poke na Jak uzyskać wyrażenie między zrównoważonymi nawiasami .
Dostosowywane przez firmę Java rozwiązanie niebędące wyrażeniem regularnym
Oto konfigurowalne rozwiązanie, które pozwala na użycie dosłownych separatorów w Javie:
Przykładowe użycie:
źródło
Wyrażenie regularne używające Ruby (wersja 1.9.3 lub nowsza):
Demo na rubularach
źródło
Potrzebujesz pierwszego i ostatniego nawiasu. Użyj czegoś takiego:
str.indexOf ('('); - da ci pierwsze wystąpienie
str.lastIndexOf (')'); - ostatni
Więc potrzebujesz ciągu między,
źródło
źródło
Odpowiedź zależy od tego, czy należy dopasować pasujące zestawy nawiasów, czy tylko od pierwszego otwarcia do ostatniego zamknięcia w tekście wejściowym.
Jeśli chcesz dopasować pasujące nawiasy klamrowe, potrzebujesz czegoś więcej niż wyrażeń regularnych. - patrz @dehmann
Jeśli to tylko pierwsze otwarcie do ostatniego zamknięcia, zobacz @Zach
Zdecyduj, co chcesz zrobić:
W takim przypadku musisz zdecydować, jaki kod będzie pasował.
źródło
ponieważ js regex nie obsługuje dopasowywania rekurencyjnego, nie mogę sprawić, by zrównoważone nawiasy były dopasowane.
więc jest to prosty javascript dla wersji pętli, który przekształca ciąg „method (arg)” w tablicę
wynik jest jak
źródło
Podczas gdy tak wiele odpowiedzi wspomina o tym w jakiejś formie, mówiąc, że regex nie obsługuje rekurencyjnego dopasowywania itd., Główny powód tego leży w korzeniach teorii obliczeń.
Język formularza
{a^nb^n | n>=0} is not regular
. Regex może pasować tylko do rzeczy, które stanowią część standardowego zestawu języków.Czytaj więcej @ tutaj
źródło
Nie użyłem wyrażenia regularnego, ponieważ trudno jest poradzić sobie z zagnieżdżonym kodem. Tak więc ten fragment kodu powinien umożliwiać przechwytywanie fragmentów kodu za pomocą zrównoważonych nawiasów:
Użyłem tego do wyodrębnienia fragmentów kodu z pliku tekstowego.
źródło
Utknąłem również w sytuacji, w której pojawiają się wzory zagnieżdżone.
Wyrażenie regularne jest właściwym rozwiązaniem powyższego problemu. Użyj poniższego wzoru
źródło
Ten też działał
źródło
Może to być przydatne dla niektórych:
Analizuj parmetery z ciągu funkcji (ze strukturami zagnieżdżonymi) w javascript
Dopasuj struktury, takie jak:
Tutaj możesz zobaczyć wygenerowane wyrażenie regularne w akcji
Nie rozwiązuje to w pełni pytania OP, ale sądzę, że niektórzy przydadzą się tutaj, aby wyszukać wyrażenie regularne struktury zagnieżdżonej.
źródło