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___'));
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
@roippi Sto passando la regex in un altro strumento in modo che abbia bisogno di essere in una singola espressione. –