2010-06-04 15 views
7

¿Puede alguien explicar:grupos de Java Matcher: Entender la diferencia entre "(?: X | Y)" y "(: X?) | (:? Y)"

  1. Por qué los dos patrones utilizados por debajo dar diferentes resultados? (Contestado a continuación)
  2. Por qué el segundo ejemplo proporciona un recuento de grupo de 1 pero dice que el inicio y el final de grupo 1 es -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); 
    } 

}

que da el siguiente resultado:

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

Respuesta

4

Para resumir,

1) Los dos patrones dan diferentes resultados debido a las reglas de precedencia de los operadores.

  • (?:Yes|No)(.*)End partidos (Sí o No) seguido de. * Fin
  • (?:Yes)|(?:No)(.*)End partidos (Sí) o (Sin seguido de. * Fin)

2) El segundo patrón da una cuenta de grupo de 1 pero un inicio y un final de -1 debido a los significados (no necesariamente intuitivos) de los resultados devueltos por las llamadas al método Matcher.

  • Matcher.find() devuelve true si se encontró una coincidencia. En su caso, la coincidencia estaba en la parte (?:Yes) del patrón.
  • Matcher.groupCount() devuelve el número de grupos de captura en el patrón independientemente de si los grupos de captura participaron realmente en la coincidencia. En su caso sólo la parte no (?:Yes) captura del patrón participó en el partido, pero el grupo (.*) captura era todavía parte del patrón por lo que el número de grupo es 1.
  • Matcher.start(n) y Matcher.end(n) devolver el índice de inicio y fin de la subsecuencia emparejado por n th grupo de captura. En su caso, aunque se encontró una coincidencia general, el grupo de captura (.*) no participó en el partido y, por lo tanto, no capturó una subsecuencia, de ahí los resultados -1.

3) (Pregunta hecha en el comentario.) Con el fin de determinar el número de grupos de captura en realidad capturan una subsecuencia, iterar Matcher.start(n) 0-Matcher.groupCount() contar el número de no -1 resultados. (Tenga en cuenta que Matcher.start(0) es el grupo de captura que representa el patrón completo, que puede excluir para sus fines).

7
  1. La diferencia es que en el segundo patrón "(?:Yes)|(?:No)(.*)End", la concatenación ("X seguido por Y" en "XY") tiene una mayor precedence que la elección ("o X o y" en "X | y"), al igual que la multiplicación tiene mayor precedencia que Además, por lo que el patrón es equivalente a

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

    lo que quería conseguir es el patrón siguiente:

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

    Esto produce el mismo resultado que el primer patrón.

    En su prueba, el segundo patrón tiene el grupo 1 en el rango (vacío) [-1, -1[ porque ese grupo no coincidía (el inicio -1 está incluido, el extremo -1 está excluido, por lo que el half-open interval está vacío).

  2. Un grupo captura es un grupo que puede entrada de captura. Si captura, también se dice que coincide con alguna subcadena de la entrada. Si la expresión regular contiene opciones, entonces no todos los grupos de captura realmente pueden capturar la entrada, por lo que puede haber grupos que no coinciden, incluso si la expresión regular coincide.

  3. El recuento de grupo, como devuelto por Matcher.groupCount(), se gana puramente contando los soportes de agrupación de grupos de captura, independientemente de si cualquiera de ellos podría coincidir en cualquier entrada dada. Su patrón tiene exactamente un grupo de captura: (.*). Este es el grupo 1. El documentation states:

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

    y explains:

    grupos que comienzan con (? son o bien puros grupos, no captura que no captan el texto y no cuentan para el total del grupo, o grupo de captura nombrada.

    Si un grupo específico coincide con una entrada dada, es irrelevante para esa definición. Por ejemplo, en el patrón (Yes)|(No), hay dos grupos ((Yes) es el grupo 1, (No) es grupo 2), pero sólo uno de ellos puede igualar para cualquier entrada dada.

  4. La llamada a Matcher.find() devuelve verdadero si la expresión regular se combinó en alguna subcadena.Puede determinar qué grupos coinciden al mirar su inicio: si es -1, entonces el grupo no coincide. En ese caso, el final también está en -1. No hay un método incorporado que le indique cuántos grupos de captura realmente coinciden después de una llamada al find() o al match(). Tendría que contarlos usted mismo mirando el comienzo de cada grupo.

  5. Cuando se trata de referencias hacia atrás, también tenga en cuenta lo the regex tutorial tiene que decir:

    hay una diferencia entre una referencia inversa a un grupo de captura que coincidía con nada, y uno a un grupo de captura que hizo no participar en el partido en absoluto.

+0

Gracias por esta respuesta. Todavía me gustaría entender por qué el recuento de grupos es 1. Comprendí (de la documentación y otros experimentos) que un recuento grupal de 1 debería significar que se había encontrado un solo grupo numerado y, por lo tanto, comenzar (1) debería ser> - 1. – user358795

+0

El recuento de grupos se gana simplemente contando los paréntesis de agrupación, y su patrón tiene exactamente uno: '(. *)'. Este es el grupo 1. Si un grupo específico coincide con una entrada dada, es irrelevante para esa definición. Por ejemplo, en el patrón '" (Sí) | (No) "', hay dos grupos ("(Sí)" es el grupo 1, "(No)" es el grupo 2), pero solo uno de ellos puede coincidir con cualquier entrada dada. –

+1

Lo que dices es que la documentación dice "Devuelve el número de grupos de captura en el patrón de este emparejador". significa el conteo en la expresión incluso si no hay coincidencia? En ese caso, ¿por qué la llamada a find() devuelve true? O para decirlo de otra manera, ¿cómo se pretende determinar si algún grupo coincide y, de ser así, cuántos? – user358795

3

Debido a la precedencia de la "|" operador en el patrón, el segundo patrón es equivalente a:

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

Lo que queremos es

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

Se equivoca acerca de groupCount, como explica claramente el Javadoc: * El grupo cero denota todo el patrón por convención. Está ** no ** incluido en este conteo. * No es intuitivo. –

+0

Ack ... estaba equivocado, eliminando de la respuesta – jimr

1

Cuando se utiliza la expresión regular es importante recordar que hay allí es un AND operador implícito en el trabajo. Esto puede ser visto desde el Javadoc para java.util.regex.Pattern que cubren los operadores lógicos:

operadores lógicos
XY X seguido de Y
X | Y X o Y
(X) X, como una captura grupo

Este AND tiene prioridad sobre la OR en el segundo patrón. El segundo Patrón es equivalente a
(?:Yes)|(?:(?:No)(.*)End).
Con el fin de que sea equivalente al primer patrón que debe ser cambiado a
(?:(?:Yes)|(?:No))(.*)End

Cuestiones relacionadas