2010-06-04 8 views
7

Quelqu'un peut-il expliquer:groupes Java Matcher: Comprendre la différence entre "(: X | Y)" et "(X?) | (: Y)"

  1. Pourquoi les deux modèles utilisés ci-dessous donner des résultats différents? (répondu ci-dessous)
  2. Pourquoi le 2ème exemple donne un compte de groupe de 1 mais indique le début et la fin du groupe 1 est -1?
public void testGroups() throws Exception 
{ 
    String TEST_STRING = "After Yes is group 1 End"; 
    { 
    Pattern p; 
    Matcher m; 
    String pattern="(?:Yes|No)(.*)End"; 
    p=Pattern.compile(pattern); 
    m=p.matcher(TEST_STRING); 
    boolean f=m.find(); 
    int count=m.groupCount(); 
    int start=m.start(1); 
    int end=m.end(1); 

    System.out.println("Pattern=" + pattern + "\t Found=" + f + " Group count=" + count + 
    " Start of group 1=" + start + " End of group 1=" + end); 
    } 

    { 
    Pattern p; 
    Matcher m; 

    String pattern="(?:Yes)|(?:No)(.*)End"; 
    p=Pattern.compile(pattern); 
    m=p.matcher(TEST_STRING); 
    boolean f=m.find(); 
    int count=m.groupCount(); 
    int start=m.start(1); 
    int end=m.end(1); 

    System.out.println("Pattern=" + pattern + "\t Found=" + f + " Group count=" + count + 
    " Start of group 1=" + start + " End of group 1=" + end); 
    } 

}

qui donne le résultat suivant:

Pattern=(?:Yes|No)(.*)End Found=true Group count=1 Start of group 1=9 End of group 1=21 
Pattern=(?:Yes)|(?:No)(.*)End Found=true Group count=1 Start of group 1=-1 End of group 1=-1

Répondre

4

En résumé,

1) Les deux modèles donnent des résultats différents en raison des règles de priorité des opérateurs.

  • (?:Yes|No)(.*)End matchs (Oui ou Non) suivi de. * Fin
  • (?:Yes)|(?:No)(.*)End matchs (Oui) ou (Non suivi. * Fin)

2) Le deuxième motif donne une nombre de groupes de 1 mais un début et une fin de -1 en raison des significations (pas nécessairement intuitives) des résultats renvoyés par les appels de méthode Matcher.

  • Matcher.find() renvoie la valeur true si une correspondance a été trouvée. Dans votre cas, le match était sur la partie (?:Yes) du motif.
  • Matcher.groupCount() renvoie le nombre de groupes de capture dans le modèle , que les groupes de capture aient réellement participé à la correspondance. Dans votre cas, seule la non capture (?:Yes) partie du motif a participé au match, mais le groupe (.*) la capture faisait encore partie du motif de sorte que le nombre de groupe est 1.
  • Matcher.start(n) et Matcher.end(n) retourne l'index de début et de fin de la sous-séquence correspond au n ème groupe de capture. Dans votre cas, bien qu'une correspondance globale ait été trouvée, le groupe de capture (.*) n'a pas participé au match et n'a donc pas capturé une sous-séquence, d'où les -1 résultats.

3) (Question posée dans le commentaire.) Afin de déterminer le nombre de groupes de capture effectivement capturé une sous, itérer Matcher.start(n) 0-Matcher.groupCount() comptant le nombre de non -1 résultats. (Notez que Matcher.start(0) est le groupe de capture représentant le motif entier, que vous pouvez exclure pour vos besoins.)

7
  1. La différence est que, dans le deuxième motif "(?:Yes)|(?:No)(.*)End", la concaténation ("X suivie Y" dans "XY") a plus de precedence que le choix ("Soit X ou Y" dans "X | Y"), comme la multiplication a une préséance plus élevée que l'addition, donc le modèle est équivalent à

    "(?:Yes)|(?:(?:No)(.*)End)" 
    

    Qu'est-ce que vous vouliez obtenir est le schéma suivant:

    "(?:(?:Yes)|(?:No))(.*)End" 
    

    Cela donne le même résultat que votre premier motif.

    Dans votre essai, le deuxième motif a le groupe 1 au (vide) vont [-1, -1[ car ce groupe ne correspond (début -1 est inclus, la fin -1 est exclue, ce qui rend le half-open interval vide).

  2. Un groupe de capture est un groupe qui peut d'entrée de capture. Si elle capture, on dit aussi correspond à une certaine sous-chaîne de l'entrée. Si la regex contient des choix, alors tous les groupes de capture ne peuvent pas réellement capturer des entrées, donc il peut y avoir des groupes qui ne correspondent pas même si les regex correspondent.

  3. Le nombre de groupe, tel qu'il est retourné par Matcher.groupCount(), est acquise uniquement en comptant les supports de regroupement des groupes de capture, indépendamment du fait que l'un d'eux pourrait correspondre à une entrée donnée. Votre motif a exactement un groupe de capture: (.*). C'est un groupe 1. Le documentation states:

    (?:X) X, as a non-capturing group 
    

    et explains:

    groupes commençant par (? soit purs, des groupes non-capture qui ne saisissent pas le texte et ne comptent pas dans le total du groupe, ou groupe de capture nommé.

    Si un groupe spécifique correspond à une entrée donnée, n'est pas pertinent pour cette définition. Par exemple, dans le modèle (Yes)|(No), il existe deux groupes ((Yes) est groupe 1, (No) est groupe 2), mais un seul d'entre eux peut correspondre pour une entrée donnée.

  4. L'appel à Matcher.find() renvoie true si l'expression regex a été mise en correspondance sur une sous-chaîne.Vous pouvez déterminer quels groupes correspondent en regardant leur début: Si c'est -1, alors le groupe ne correspond pas. Dans ce cas, la fin est également à -1. Il n'y a pas de méthode intégrée qui vous indique combien de groupes de capture ont réellement correspondu après un appel à find() ou match(). Vous devrez les compter vous-même en regardant le début de chaque groupe.

  5. En ce qui concerne les références arrières, notez aussi ce que the regex tutorial a à dire:

    Il y a une différence entre un backreference à un groupe de capture qui correspondait à rien, et un à un groupe de capture qui a fait ne pas participer au match du tout.

+0

Merci pour cette réponse. Je voudrais toujours comprendre pourquoi le compte de groupe est 1. J'ai compris (de la documentation et d'autres expériences) qu'un compte de groupe de 1 devrait signifier qu'un seul groupe numéroté a été trouvé et donc commencer (1) devrait être> - 1. – user358795

+0

Le nombre de groupes est obtenu uniquement en comptant les parenthèses de regroupement, et votre modèle en a exactement un: '(. *)'. C'est le groupe 1. Si un groupe spécifique correspond à une entrée donnée, n'est pas pertinent pour cette définition. Par exemple, dans le modèle '" (Oui) | (Non) "', il y a deux groupes ("(Oui)" est le groupe 1, "(Non)" est le groupe 2), mais un seul peut correspondre donnée donnée. –

+1

Donc vous dites que là où la documentation dit "Renvoie le nombre de groupes de capture dans le modèle de ce matcher". cela signifie le compte dans l'expression même s'il n'y a pas de correspondance? Dans ce cas, pourquoi l'appel de find() retourne-t-il vrai? Ou, pour le dire autrement, comment est-ce qu'on a l'intention de déterminer si des groupes ont correspondu et si oui, combien? – user358795

3

En raison de la priorité du "|" opérateur dans le motif, le second motif est équivalent à:

(?:Yes)|((?:No)(.*)End) 

Qu'est-ce que vous voulez est

(?:(?:Yes)|(?:No))(.*)End 
+0

Vous avez tort à propos de groupCount, comme l'explique clairement Javadoc: * Le groupe zéro désigne le modèle entier par convention. Ce n'est pas ** inclus dans ce compte. * Ce n'est pas intuitif. –

+0

Ack .. j'avais tort, en supprimant de la réponse – jimr

1

Lorsque vous utilisez l'expression régulière est-il important de se rappeler qu'il ya un opérateur AND implicite au travail. Ceci peut être vu à partir de la JavaDoc pour java.util.regex.Pattern couvrant les opérateurs logiques:

Les opérateurs logiques
XY X suivi de Y
X | Y X ou Y
(X) X, en tant que capture groupe

Cette AND a priorité sur OR dans le deuxième motif. Le second motif est équivalent à
(?:Yes)|(?:(?:No)(.*)End).
Pour qu'il soit équivalent au premier motif, il doit être changé pour
(?:(?:Yes)|(?:No))(.*)End

Questions connexes