2014-05-21 14 views
5

Non sono sicuro che ciò sia possibile utilizzando Regex, ma mi piacerebbe poter limitare il numero di caratteri di sottolineatura consentiti in base a un carattere diverso. Questo per limitare le pazzesche query con caratteri jolly a un motore di ricerca scritto in Java.Regex per abbinare e limitare le classi di caratteri

I caratteri iniziali sono alfanumerici. Ma fondamentalmente voglio una corrispondenza se ci sono più caratteri di sottolineatura rispetto ai caratteri precedenti. Quindi

BA_ andrebbe bene ma BA___ corrisponderebbe all'espressione regolare e verrebbe espulso dal parser della query.

È possibile utilizzare Regex?

+1

Personalmente abbinerei le lettere in un gruppo, i caratteri di sottolineatura in un altro, quindi asserisco che la lunghezza del secondo gruppo è inferiore a quella del primo. – roippi

+0

@roippi Sto passando la regex in un altro strumento in modo che abbia bisogno di essere in una singola espressione. –

risposta

8

Sì, puoi farlo. Questo modello avrà successo solo se ci sono meno di sottolineatura di lettere (è possibile adattare con i personaggi che si desidera):

^(?:[A-Z](?=[A-Z]*(\\1?+_)))*+[A-Z]+\\1?$ 

(come Pshemo se ne accorge, ancore non sono necessari se si utilizza il metodo matches() , Li ho scritti per illustrare il fatto che questo modello deve essere limitato in qualunque modo.)

versione negata:

^(?:[A-Z](?=[A-Z]*(\\1?+_)))*\\1?_*$ 

L'idea è quella di ripetere un gruppo di cattura che contiene un backreference a se stesso + un carattere di sottolineatura. Ad ogni ripetizione, il gruppo di cattura sta crescendo. ^(?:[A-Z](?=[A-Z]*+(\\1?+_)))*+ corrisponderà a tutte le lettere che hanno un carattere di sottolineatura corrispondente. Devi solo aggiungere [A-Z]+ per assicurarti che ci siano più lettere e per completare il tuo pattern con \\1? che contenga tutti i caratteri di sottolineatura (lo rendo opzionale, nel caso in cui non vi sia alcun carattere di sottolineatura).

Si noti che se si sostituisce [A-Z]+ con [A-Z]{n} nel primo modello, è possibile impostare esattamente il numero di caratteri di differenza tra lettere e caratteri di sottolineatura.


Per rendere meglio l'idea, cercherò di descrivere passo dopo passo come funziona con la stringa ABC-- (dal momento che è impossibile mettere sottolineatura in grassetto, io uso trattini invece):

 
 In the non-capturing group, the first letter is found 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
 let's enter the lookahead (keep in mind that all in the lookahead is only 
a check and not a part of the match result.) 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
 the first capturing group is encounter for the first time and its content is not 
defined. This is the reason why an optional quantifier is used, to avoid to make 
the lookahead fail. Consequence: \1?+ doesn't match something new. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
 the first hyphen is matched. Once the capture group closed, the first capture 
    group is now defined and contains one hyphen. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
The lookahead succeeds, let's repeat the non-capturing group. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
The second letter is found 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
We enter the lookahead 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
but now, things are different. The capture group was defined before and 
contains an hyphen, this is why \1?+ will match the first hyphen. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
the literal hyphen matches the second hyphen in the string. And now the 
capture group 1 contains the two hypens. The lookahead succeeds. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
We repeat one more time the non capturing group. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
In the lookahead. There is no more letters, it's not a problem, since 
the * quantifier is used. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
\\1?+ matches now two hyphens. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
but there is no more hyphen in the string for the literal hypen and the regex engine can not use the bactracking since \1?+ has a possessive quantifier. 
The lookahead fails. Thus the third repetition of the non-capturing group too! 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
ensure that there is at least one more letter. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$
 
We match the end of the string with the backreference to capture group 1 that 
contains the two hyphens. Note that the fact that this backreference is optional 
allows the string to not have hyphens at all. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 
This is the end of the string. The pattern succeeds. 
ABC--  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*+[A-Z]+\1?$ 


Nota: è necessario l'uso del quantificatore possessivo per il gruppo non-cattura per evitare falsi risultati. (dove è possibile osservare uno strano comportamento, che può essere utile.)

Esempio: ABC--- e il modello: ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$ (senza il quantificatore possessivo)

 
The non-capturing group is repeated three times and `ABC` are matched: 
ABC---  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$ 
Note that at this step the first capturing group contains --- 
But after the non capturing group, there is no more letter to match for [A-Z]+ 
and the regex engine must backtrack. 
ABC---  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$ 

Domanda: Quanti i trattini sono ora nel gruppo di cattura?
Risposta:   Sempre tre!

Se il gruppo non catturato ripetuto restituisce una lettera, il gruppo di cattura contiene sempre tre trattini (come l'ultima volta che il gruppo di cattura è stato letto dal motore regex). Questo è contro-intuitivo, ma logico.

 
Then the letter C is found: 
ABC---  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$ 
And the three hyphens 
ABC---  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$ 
The pattern succeeds 
ABC---  ^(?:[A-Z](?=[A-Z]*(\1?+-)))*[A-Z]+\1?$

Robby Stagno mi ha chiesto nei commenti come trovare stringhe che hanno più di sottolineatura di lettere (tutto ciò che non è un carattere di sottolineatura). Il modo migliore è ovviamente contare i numeri dei caratteri di sottolineatura e confrontarli con la lunghezza della stringa. Ma riguardo a una soluzione regex completa, non è possibile creare un modello per quello con Java poiché il pattern deve utilizzare la funzione di ricorsione.Ad esempio, puoi farlo con PHP:

$pattern = <<<'EOD' 
~ 
(?(DEFINE) 
    (?<neutral> (?: _ \g<neutral>?+ [A-Z] | [A-Z] \g<neutral>?+ _)+) 
) 

\A (?: \g<neutral> | _)+ \z 
~x 
EOD; 

var_dump(preg_match($pattern, '____ABC_DEF___')); 
+0

Quale sapore di regex è questo? Lo trovo interessante Potresti spiegare un po 'di più? Trovo difficile capire la parte ricorsiva. Regex101 non aiuta davvero. Non corrisponde nemmeno lì. Potete fornire un sito Web in cui è possibile testare la regex di cui sopra? –

+0

@FarhadAliNoo: è una versione per Java, ma la stessa tecnica può essere utilizzata anche con PCRE. Date un'occhiata a questo post: http://stackoverflow.com/questions/23001137/capturing-quantifiers-and-quantifier-arithmetic/23002044#23002044 –

+1

@FarhadAliNoo: proverò a spiegare di più. –

0

Non è possibile nell'espressione regolare singolare.

i) La logica deve essere implementata per ottenere il numero di caratteri prima dei caratteri di sottolineatura (l'espressione regolare deve essere scritta per ottenere caratteri prima della sottolineatura).

ii) E conferma risultato (numero di caratteri - 1) = numero di punti e virgola seguiti (espressione regolare che restituisce il flusso di caratteri di sottolineatura seguiti da caratteri).

0

Modifica: Dang! Ho appena notato che ne hai bisogno per Java. Comunque ... Lascio qui se qualcuno del mondo .Net inciampa su questo post.

È possibile utilizzare Balancing Groups se si sta utilizzando .Net: motore regex

^(?:(?<letter>[^_])|(?<-letter>_))*(?(letter)(?=)|(?!))$ 

.NET ha la capacità di mantenere tutti i modelli catturati nei gruppi catturati. In altri sapori il gruppo catturato conterrà sempre l'ultimo pattern abbinato ma in .net tutte le partite precedenti sono contenute in una collezione di cattura per il tuo uso. Anche il motore .net ha la possibilità di spingere e far apparire la pila dei gruppi catturati usando i costrutti ?<group-name>, ?<-group-name>. Questi due utili costrutti possono essere utilizzati per abbinare coppie di paratessi, ecc.

Nella regex precedente, il motore parte dall'inizio della stringa e tenta di far corrispondere qualsiasi valore diverso da "_". Questo, ovviamente, può essere modificato in qualsiasi funzione per te (ad esempio [A-Z][a-z]). L'alternanza significa fondamentalmente o corrisponde a [^\_] o [\_] e così facendo push o pop dal gruppo catturato.

L'ultima parte della regex è un condizionale (?(group-name)true|false). In pratica dice, se il gruppo esiste ancora (più push che pop), quindi fai la vera sezione e se no fai la falsa sezione. Il modo più semplice per far corrispondere il modello è usare un aspetto positivo vuoto: (?=) e il modo più semplice per farlo fallire è (?!) che è un aspetto negativo.

Problemi correlati