2012-11-15 14 views
12

TL; DR: Uso cattura (in particolare i gruppi di bilanciamento) all'interno del lookbehinds NET cambia le catture ottenuti, anche se non dovrebbe fare una differenza. Che cosa è con le schermate di .NET che rompono il comportamento previsto?gruppi Balancing a lunghezza variabile lookbehind

Stavo cercando di trovare una risposta a this other question, come una scusa per giocare con i gruppi di bilanciamento di .NET. Tuttavia, non riesco a farli funzionare all'interno di un lookbehind a lunghezza variabile.

Prima di tutto, si noti che non intendo utilizzare questa particolare soluzione in modo produttivo. È più per ragioni accademiche, perché sento che sta succedendo qualcosa con lo sguardo a lunghezza variabile dietro il quale non sono a conoscenza. E sapendo che potrebbe tornare utile in futuro, quando effettivamente devo usare qualcosa del genere per risolvere un problema.

Considerate questo ingresso:

~(a b (c) d (e f (g) h) i) j (k (l (m) n) p) q 

L'obiettivo è quello di abbinare tutte le lettere, che sono tra parentesi all'interno preceduti da ~, non importa quanto in profondità (quindi tutto a-i). Il mio tentativo era quello di verificare la posizione corretta in un lookbehind, in modo da poter ottenere tutte le lettere in una singola chiamata a Matches. Qui è il mio modello:

(?<=~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*)[a-z] 

Nel lookbehind cerco di trovare un ~(, e poi io uso la pila gruppo denominato Depth contare parentesi di apertura estranei. Finché la parentesi aperta in ~( non viene mai chiusa, il lookbehind deve corrispondere. Se la parentesi di chiusura viene raggiunta, lo (?<-Depth>...) non può estrarre nulla dallo stack e il lookbehind non dovrebbe riuscire (ovvero, per tutte le lettere da j). Sfortunatamente, questo non funziona. Invece, combacio con a, b, c, e, f, g e m. Quindi, solo questi:

~(a b (c) _ (e f (g) _) _) _ (_ (_ (m) _) _) _ 

Questo sembra significare che il lookbehind non può corrispondere nulla una volta che ho chiuso una sola parentesi, a meno che non torno giù al livello di nidificazione più alto Sono stato a prima.

Ok, questo potrebbe significare solo che c'è qualcosa di strano con la mia espressione regolare, o non ho capito correttamente i gruppi di bilanciamento. Ma poi ho provato questo senza il lookbehind. Ho creato una stringa per ogni lettera come questa:

~(z b (c) d (e f (x) y) g) h (i (j (k) l) m) n 
~(a z (c) d (e f (x) y) g) h (i (j (k) l) m) n 
~(a b (z) d (e f (x) y) g) h (i (j (k) l) m) n 
.... 
~(a b (c) d (e f (x) y) g) h (i (j (k) l) z) n 
~(a b (c) d (e f (x) y) g) h (i (j (k) l) m) z 

E usato questo modello su ciascuno di questi:

~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*z 

E, se lo desideri, tutti i casi match, in cui z sostituisce una lettera tra a e i e tutti i casi successivi falliscono.

Quindi che cosa fa il look-behind (a lunghezza variabile) che interrompe questo uso di gruppi di bilanciamento? Ho provato a cercare questo per tutta la serata (e ho trovato pagine come this one), ma non sono riuscito a trovare un singolo utilizzo di questo in una schermata.

Sarei anche felice se qualcuno potesse collegarmi ad alcune informazioni approfondite su come il motore regex .NET gestisce internamente le funzionalità specifiche di .NET. Ho trovato this amazing article, ma non sembra entrare nei lookbehinds (a lunghezza variabile), per esempio.

+0

vedi [divertimento con .NET Regex Balancing gruppi] (http://blog.stevenlevithan.com/archivi/gruppi di bilanciamento). – OmegaMan

+0

@OmegaMan, grazie, ma l'ho trovato anche io (in realtà quella era la pagina su cui ho basato il mio codice). Sfortunatamente, anche questo non usa gruppi bilanciati in una schermata. –

+0

quindi, un singolo risultato come "a b (c) d (e f (g) h) i" è accettabile o deve essere 9 corrisponde a, b, c, d, e, f, g, h e i? – Les

risposta

9

Penso di averlo capito.
Innanzitutto, come ho menzionato in uno dei commenti, (?<=(?<A>.)(?<-A>.)) non corrisponde mai.
Ma poi ho pensato, che dire di (?<=(?<-A>.)(?<A>.))? Corrisponde!
E che ne dite di (?<=(?<A>.)(?<A>.))? Corrispondenza con "12", A acquisisce "1" e, se guardiamo la collezione Captures, è {"2", "1"} - prima due, poi una - è invertita.
Quindi, mentre all'interno di un lookbehind, .net corrisponde e cattura da destra a sinistra.

Ora, come possiamo catturarlo da sinistra a destra? Questo è abbastanza semplice, in realtà - siamo in grado di ingannare il motore con un lookahead:

(?<=(?=(?<A>.)(?<A>.))..) 

applicati sulla Patten originale, l'opzione più semplice mi è venuta era:

(?<= 
    ~[(] 
    (?= 
     (?: 
      [^()] 
      | 
      (?<Depth>[(]) 
      | 
      (?<-Depth>[)]) 
     )* 
     (?<=(\k<Prefix>)) # Make sure we matched until the current position 
    ) 
    (?<Prefix>.*)   # This is captured BEFORE getting to the lookahead 
) 
[a-z] 

La sfida qui era che ora la parte bilanciata può finire ovunque, quindi lo facciamo arrivare fino alla posizione corrente (Qualcosa come \G o \Z sarebbe utile qui, ma non credo .net ha quello)

È molto possibile questo comportamento è documente da qualche parte, cercherò di cercarlo.

Ecco un altro approccio. L'idea è semplice -.la rete vuole corrispondere da destra a sinistra? Bene! Prendere che:
(suggerimento: iniziare la lettura dal basso - che è come .net lo fa)

(?<= 
    (?(Depth)(?!)) # 4. Finally, make sure there are no extra closed parentheses. 
    ~\(
    (?>      # (non backtracking) 
     [^()]    # 3. Allow any other character 
     | 
     \((?<-Depth>)?  # 2. When seeing an open paren, decreace depth. 
          # Also allow excess parentheses: '~((((((a' is OK. 
     | 
     (?<Depth> \))  # 1. When seeing a closed paren, add to depth. 
    )* 
) 
\w       # Match your letter 
+0

Non riesco a trovare nulla su msdn, nemmeno su [Modalità da destra a sinistra ] (http://msdn.microsoft.com/en-us/library/yd1hzczs.aspx#RightToLeft). Molto minimale Forse è sul BCL. – Kobi

+0

Wow, è grandioso. Vedi, ho notato l'ordine inverso delle catture, ma ho pensato che fosse in qualche modo dovuto alla natura simile allo stack dei gruppi (che è, ovviamente, un'assurdità assoluta, ma la corrispondenza da destra a sinistra non mi è venuta in mente a tutti). Ci giocherò domani con questo per vedere quanto sia affidabile e se riesco a trovare altri metodi. Grazie mille per aver dedicato del tempo per indagare su questo! –

+2

Appena trovato [questo] (https://mail.mozilla.org/pipermail/es-discuss/2012-March/021378.html). Contenente (tra le altre cose) "La modalità da destra a sinistra che alimenta l'aspetto di .NET ...". E [questo libro] (http://books.google.co.uk/books?id=6k7IfACN_P8C&pg=PA86&lpg=PA86&dq=.net + lookbehind + da destra a sinistra & source = bl & ots = CoJb4Jnl2N & sig = 4h38arO1NgCvCdTy3fx4zDa4yx8 & hl = de & sa = X & ei = NNKmUOaCFYKd0QW05YHoCA & ved = 0CEYQ6AEwBA # v = onepage & q = .net% 20lookbehind% 20right-to-left & f = false) lo conferma anche. –

2

Penso che il problema sia con i dati e non con il modello. I dati sono elementi 'post' che devono essere abbinati come

(a b (c) d e f)

in cui sono necessari d e ed f da abbinare. A più equilibrati dati sarebbero

(ab (c) (d) (e) (f))


Quindi la virata ho assunto questo esempio dati richiesti un match situazione post dopo la parentesi graffe :

~ (ab (c) d (ef (g) h) i) jk

dove j k & dovrebbe essere ignorato ... il mio modello di guasti e catturati.

La cosa interessante è che ho chiamato i gruppi di cattura per scoprire dove sono entrati e j e k sono entrati nella cattura tre. Vi lascio con, non una risposta, ma un tentativo di vedere se è possibile migliorare su di esso.

(~       # Anchor to a Tilde 
(      # Note that \x28 is (and \x29 is)  
    (       # --- PRE --- 
    (?<Paren>\x28)+   # Push on a match into Paren 
    ((?<Char1>[^\x28\x29])(?:\s?))* 
    )+       # Represents Sub Group 1 
    (       #---- Closing 
    ((?<Char2>[^\x28\x29])(?:\s?))* 
    (?<-Paren>\x29)+   # Pop off a match from Paren 

)+ 
    (
    ((?<Char3>[^\x28\x29])(?:\s?))* # Post match possibilities 
)+ 

)+ 
(?(Paren)(?!)) # Stop after there are not parenthesis  
) 

Ecco la partita scoppiata con uno strumento che ho creato da solo (forse un giorno pubblicherò). Si noti che ˽ mostra dove è stato abbinato uno spazio.

Match #0 
       [0]: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k 
     ["1"] → [1]: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k 
     →1 Captures: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k 
     ["2"] → [2]: (e˽f˽(g)˽h)˽i)˽j˽k 
     →2 Captures: (a˽b˽(c)˽d˽, (e˽f˽(g)˽h)˽i)˽j˽k 
     ["3"] → [3]: (g 
     →3 Captures: (a˽b˽, (c, (e˽f˽, (g 
     ["4"] → [4]: g 
     →4 Captures: a˽, b˽, c, e˽, f˽, g 
     ["5"] → [5]: ˽i) 
     →5 Captures: ),), ˽h), ˽i) 
     ["6"] → [6]: i 
     →6 Captures: ˽, h, ˽, i 
     ["7"] → [7]: 
     →7 Captures: ˽d˽, , ˽j˽k, 
     ["8"] → [8]: k 
     →8 Captures: ˽, d˽, ˽, j˽, k 
    ["Paren"] → [9]: 
    ["Char1"] → [10]: g 
     →10 Captures: a, b, c, e, f, g 
    ["Char2"] → [11]: i 
     →11 Captures: ˽, h, ˽, i 
    ["Char3"] → [12]: k 
     →12 Captures: ˽, d, ˽, j, k 
+0

Questo è uno strumento davvero utile! Tuttavia, penso che il motivo per cui si ottiene una corrispondenza per 'j' e' k' è semplicemente che si conta la prima apertura '(' nello stack 'Parens', quindi è perfettamente valido rimuoverlo di nuovo. nel mio pattern, non conto il '(' in '~ (' quindi non dovrei mai essere in grado di chiuderlo. (E se non uso un lookbehind, ma lo uso come uno schema semplice come hai fatto tu , il mio bilanciamento sembra funzionare perfettamente). In ogni caso, grazie per l'idea di usare gruppi di cattura denominati per qualsiasi cosa per scoprire di più sulla partita, ne darò un'occhiata! –

+0

@ m.buettner Era il l'ultima cosa che ho fatto la scorsa notte, quindi il post non è stato così conciso come avrei potuto fare, ma il tuo problema mi ha intrigato abbastanza da postare. – OmegaMan

+0

E sono sinceramente grato per il tuo contributo;). Ti farò sapere quando qualsiasi cosa provenga dall'analisi di più acquisizioni con il tentativo di lookbehind. –

Problemi correlati