Domanda

diciamo Let abbiamo la seguente entrata:

<amy>
(bob)
<carol)
(dean>

Abbiamo anche la seguente espressione regolare:

<(\w+)>|\((\w+)\)

Ora abbiamo due partite ( come visto in rubular.com ):

  • <amy> è una partita, cattura \1 amy, \2 fallisce
  • (bob) è una partita, cattura \2 bob, \1 fallisce

Questa espressione regolare fa la maggior parte di ciò che vogliamo, che sono:

  • Si abbina le staffe di apertura e chiusura correttamente (cioè nessuna miscelazione)
  • Cattura la parte che ci interessa

Tuttavia, ha alcuni svantaggi:

  • Il modello di cattura (vale a dire la parte "principale") si ripete
    • E '\w+ solo in questo caso, ma in generale questo può essere molto complessa,
      • Se si tratta backreference, allora devono essere rinumerate per ogni alternativa!
      • La ripetizione rende la manutenzione un incubo! (Cosa succede se si cambia?)
  • I gruppi sono sostanzialmente duplicati
    • A seconda di quale partite alterne, dobbiamo interrogare diversi gruppi
      • E 'solo \1 o \2 in questo caso, ma in genere la parte "principale" può avere catturare gruppi di loro!
    • Non solo questo è scomodo, ma ci possono essere situazioni in cui ciò non sia possibile (per esempio quando stiamo usando un quadro regex personalizzato che si limita a interrogare un solo gruppo)
  • La situazione peggiora rapidamente se vogliamo anche abbinare {...}, [...], ecc.

Quindi la domanda è ovvia:? come possiamo fare questo senza ripetere lo schema "principale"

  

. Nota: per la maggior parte che mi interessa nel sapore java.util.regex, ma altri sapori vengono accolti


Appendice

Non c'è nulla di nuovo in questa sezione; illustra solo il problema di cui sopra con un esempio.

Prendiamo l'esempio precedente alla fase successiva: ora vogliamo abbinare questi:

<amy=amy>
(bob=bob)
[carol=carol]

Ma non questi:

<amy=amy)   # non-matching bracket
<amy=bob>   # left hand side not equal to right hand side

Utilizzando la tecnica alternativa, abbiamo la seguente che le opere ( come visto in rubular.com ):

<((\w+)=\2)>|\(((\w+)=\4)\)|\[((\w+)=\6)\]

Come spiegato in precedenza:

  • Il modello principale non può semplicemente essere ripetuto; backreference devono essere rinumerate
  • La ripetizione significa anche la manutenzione incubo se si cambia mai
  • A seconda di quale partite alterne, dobbiamo interrogare sia \1 \2, \3 \4, o \5 \6
È stato utile?

Soluzione

È possibile utilizzare un lookahead di "lock in" il numero del gruppo prima di fare la partita vera.

String s = "<amy=amy>(bob=bob)[carol=carol]";
Pattern p = Pattern.compile(
  "(?=[<(\\[]((\\w+)=\\2))(?:<\\1>|\\(\\1\\)|\\[\\1\\])");
Matcher m = p.matcher(s);

while(m.find())
{
  System.out.printf("found %s in %s%n", m.group(2), m.group());
}

uscita:

found amy in <amy=amy>
found bob in (bob=bob)
found carol in [carol=carol]

E 'ancora brutto come l'inferno, ma non è necessario ricalcolare tutti i numeri di gruppo ogni volta che si apporta una modifica. Ad esempio, per aggiungere il supporto per le parentesi graffe, è solo:

"(?=[<(\\[{]((\\w+)=\\2))(?:<\\1>|\\(\\1\\)|\\[\\1\\]|\\{\\1\\})"

Altri suggerimenti

In preg (Perl biblioteca Regex), questo sarà abbinare il vostro esempio, e \3 prenderà le parti interne:

((<)|\()(\w+)(?(2)>|\))

Non funzionerà in JS, anche se - non è stato specificato il dialetto ...

Dipende (?(2)...|...) operatore condizionale che sostanzialmente dice che se 2 è un bloccaggio non nullo, allora corrispondere prima che il tubo, altrimenti partita dopo che il tubo. In questa forma, il tubo è non alternanza ( "o").

Aggiorna Mi dispiace, ho completamente perso il Java bit :) In ogni caso, a quanto pare Java non supporta il costrutto condizionale; e non ho idea di come il resto mi piacerebbe andare a questo proposito: (

Inoltre, per il vostro appendice (anche se è sbagliato il dialetto):

(?:(<)|(\()|\[)(\w+)=\3(?(1)>|(?(2)\)|]))

Il nome è di nuovo in \3 (mi sono liberato delle prime parentesi di cattura, ma ho dovuto aggiungere un altro uno per uno paren di apertura in più verificare)

L'unica soluzione che ho potuto venire in mente è ispirata dalla tecnica di catturare una stringa vuota su diversi sostituti; riferimento all'indietro a questi gruppi in seguito può servire come pseudo-condizionali.

Pertanto, questo modello funziona per il secondo esempio ( come visto in rubular.com ) :

                  __main__
                 /        \
(?:<()|\(()|\[())((\w+)=\5)(\1>|\2\)|\3\])
\_______________/          \_____________/
    \1   \2   \3

Quindi, in sostanza per ogni staffa apertura, si assegna un gruppo che cattura una stringa vuota. Poi quando cerchiamo di abbinare alla staffa di chiusura, vediamo quale gruppo ha avuto successo, e corrisponde alla staffa di chiusura corrispondente.

La parte "principale" non deve essere ripetuto, ma in Java, backreference potrebbe dover essere rinumerato. Questo non sarà un problema in sapori che il supporto di nome gruppi.

Può essere questo esempio in Perl vi interesserà:

$str = q/<amy=amy> (bob=bob) [carol=carol] <amy=amy) <amy=bob>/;
$re = qr/(?:<((\w+)=\2)>|\(((\w+)=\4)\)|\[((\w+)=\6)\])+/;
@list = ($str =~ /$re/g);
for(@list) {
    say $i++," = ",$_;
}

Ho appena circondano la vostra regex per (:? Regex) +

Quando si ottiene cose come questa, utilizzando un unico regex è una restrizione stupido, e ho semplicemente non sono d'accordo con la vostra "incubo di manutenzione" per utilizzare più di un - ripetere una simile espressione-but-diverso più volte è probabile di essere più mantenibile (beh, meno impossibile da mantenere), e forse anche migliori prestazioni anche di un singolo regex eccessivamente complesso.

Ma in ogni caso, non c'è ripetizione se si utilizza le variabili per comporre il regex.

Ecco alcuni pseudo-codice:

Brackets = "<>,(),[]"
CoreRegex = "(\w+)=\1"

loop CurBracket in Brackets.split(',')
{
    Input.match( Regex.quote(CurBracket.left(1)) & CoreRegex & Regex.quote(CurBracket.right(1)) )
}


(p.s.that è solo per dare l'idea generale - probabilmente userei array già fuggiti per i set di staffe di effettiva attuazione).

Supponendo non esiste un modo facile scrivere manualmente questa espressione regolare, perché non lasciare che sia il computer? Si potrebbe avere una funzione, forse come di seguito (sto usando C # la sintassi qui, come io sono un po 'più familiarità con le regex qui che in Java, ma non dovrebbe essere troppo difficile da adattarlo a Java).

Si noti che ho lasciato le AdaptBackreferences funzione () più o meno non implementato come un esercizio per il lettore . Si deve solo adattare la numerazione backreference.

    struct BracketPair {public string Open; public string Close;};

    static string[] MatchTextInBrackets(string text, string innerPattern, BracketPair[] bracketPairs) {
        StringBuilder sb  = new StringBuilder();

        // count number of catching parentheses of innerPattern here:
        int numberOfInnerCapturingParentheses = Regex.Match("", innerPattern).Groups.Count - 1;

        bool firstTime = true;
        foreach (BracketPair pair in bracketPairs) {
            // apply logic to change backreference numbering:
            string adaptedInnerPattern = AdaptBackreferences(innerPattern);
            if (firstTime) { firstTime = false; } else { sb.Append('|'); }
            sb.Append(pair.Open).Append("(").Append(adaptedInnerPattern).Append(")").Append(pair.Close);
        }
        string myPattern = sb.ToString();
        MatchCollection matches = Regex.Matches(text, myPattern);
        string[] result = new string[matches.Count];
        for(int i=0; i < matches.Count; i++) {
            StringBuilder mb = new StringBuilder();
            for(int j=0; j < bracketPairs.Length; j++) {
                mb.Append(matches[i].Groups[1 + j * (numberOfInnerCapturingParentheses + 1)]); // append them all together, assuming all exept one are empty
            }
            result[i] = mb.ToString();
        }
        return result;
    }

    static string AdaptBackreferences(string pattern) { return pattern; } // to be written
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top