2013-08-14 10 views
11

In Perl regex, espressioni come \1, \2, ecc. Vengono solitamente interpretate come "riferimenti secondari" a gruppi precedentemente acquisiti, ma non così quando appaiono \1, \2, ecc. All'interno di una classe di caratteri. In quest'ultimo caso, il \ viene considerato come un carattere di escape (e pertanto \1 è solo 1, ecc.).Approccio generale per (equivalente a) "backreferences within character class"?

Pertanto, se (ad esempio) si volesse abbinare una stringa (di lunghezza superiore a 1), il cui primo carattere corrisponde il suo ultimo carattere, ma non sembra in qualsiasi altra parte della stringa, la seguente espressione regolare sarà non fare :

/\A  # match beginning of string; 
(.)  # match and capture first character (referred to subsequently by \1); 
[^\1]* # (WRONG) match zero or more characters different from character in \1; 
\1  # match \1; 
\z  # match the end of the string; 
/sx  # s: let . match newline; x: ignore whitespace, allow comments 

sarebbe non lavoro, dal momento che corrisponde (per esempio) la stringa 'a1a2a':

DB<1> ('a1a2a' =~ /\A(.)[^\1]*\1\z/ and print "fail!") or print "success!" 
fail! 

solito riesco a riuscire a trovare alcuni workaround , ma è sempre piuttosto specifico per il problema, e di solito molto più complicato di quello che farei se potessi usare i backreferences all'interno di una classe di caratteri.

Esiste una soluzione generale (e, si spera, diretta)?


Ad esempio, per il problema nell'esempio di cui sopra, mi piacerebbe usare qualcosa di simile a

/\A 
(.)    # match and capture first character (referred to subsequently 
        # by \1); 
(?!.*\1\.+\z) # a negative lookahead assertion for "a suffix containing \1"; 
.*    # substring not containing \1 (as guaranteed by the preceding 
        # negative lookahead assertion); 
\1\z    # match last character only if it is equal to the first one 
/sx 

... dove ho sostituito il ragionevolmente semplice (sebbene, ahimè, non corretto) subexpression [^\1]* nella regex precedente con l'affermazione lookahead negativa un po 'più proibitiva (?!.*\1.+\z). Questa affermazione in pratica dice "rinunciare se \1 appare ovunque oltre questo punto (tranne che nell'ultima posizione)." Per inciso, ho dato questa soluzione solo per illustrare il tipo di soluzioni alternative a cui ho fatto riferimento nella domanda. Non sostengo che sia particolarmente buono.

+0

La soluzione accettata è perfetta per la negazione, ma non coprirà altri usi delle classi di caratteri, ad esempio intervalli. Supponiamo di voler abbinare tutte le sequenze di 3 cifre in ordine decrescente (quindi "111", "123", "368", "449", ma non "987" o "322"). Usando il backrif nelle classi di caratteri, la pseudo-regex sarebbe '/^([0-9]) ([\ 1-9]) ([\ 2-9]) $ /', ma non è possibile realizzare lo stesso semplicemente con un lookahead negativo. – rampion

risposta

10

Questo può essere realizzato con un lookahead negativo all'interno di un gruppo ripetuto:

/\A   # match beginning of string; 
(.)  # match and capture first character (referred to subsequently by \1); 
((?!\1).)* # match zero or more characters different from character in \1; 
\1   # match \1; 
\z   # match the end of the string; 
/sx 

Questo modello può essere utilizzato anche se il gruppo contiene più di un carattere.

Problemi correlati