Java Matcher组:理解"(?:X | Y)"和"(?:X)|(?:Y)"之间的区别

use*_*795 7 java regex regex-group

谁能解释一下:

  1. 为什么下面使用的两种模式给出不同的结果?(以下回答)
  2. 为什么第二个例子的组计数为1,但是组1的开始和结束是-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 );
  }

 }
Run Code Online (Sandbox Code Playgroud)

其中给出了以下输出:

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
Run Code Online (Sandbox Code Playgroud)

Chr*_*rau 8

  1. 不同之处在于,在第二种模式中"(?:Yes)|(?:No)(.*)End",串联("X后跟Y"中的"X")优先于选择("X | Y"中的"X或Y"),如乘法优先级高于另外,所以模式相当于

    "(?:Yes)|(?:(?:No)(.*)End)"
    
    Run Code Online (Sandbox Code Playgroud)

    你想得到的是以下模式:

    "(?:(?:Yes)|(?:No))(.*)End"
    
    Run Code Online (Sandbox Code Playgroud)

    这产生与第一个模式相同的输出.

    在您的测试中,第二个模式在(空)范围内具有组1,[-1, -1[因为该组不匹配(包括开始-1,排除结束-1,使半开放间隔为空).

  2. 捕获组是一组可以捕获输入.如果它捕获,则还表示它匹配输入的某些子串.如果正则表达式包含选项,则不是每个捕获组都可能实际捕获输入,因此即使正则表达式匹配,也可能存在不匹配的组.

  3. 返回的组计数Matcher.groupCount()纯粹是通过计算捕获组的分组括号来获得的,而不管它们中的任何一个是否可以匹配任何给定的输入.您的模式只有一个捕获组:(.*).这是第1组.文档说明:

    (?:X)    X, as a non-capturing group
    
    Run Code Online (Sandbox Code Playgroud)

    解释说:

    以...开头的组(?是纯粹的非捕获组,不捕获文本,不计入组总数或命名捕获组.

    任何特定组是否与给定输入匹配,与该定义无关.例如,在模式中(Yes)|(No),有两个组((Yes)组1,(No)组2),但只有一个组可以匹配任何给定的输入.

  4. Matcher.find()如果正则表达式在某些子字符串上匹配,则调用返回true.您可以通过查看其开头来确定匹配的组:如果它是-1,则该组不匹配.在这种情况下,结尾也是-1.没有内置方法可以告诉您在调用find()或之后实际匹配了多少个捕获组match().你必须通过观察每个小组的开始来自己计算.

  5. 在反向引用时,还要注意正则表达式教程的含义:

    对没有匹配的捕获组的反向引用与根本没有参与匹配的捕获组之间存在差异.


Hwe*_*wee 5

总而言之,

1)由于运算符的优先规则,这两种模式给出不同的结果。

  • (?:Yes|No)(.*)End 匹配(是或否)后跟。* End
  • (?:Yes)|(?:No)(.*)End 匹配(是)或(否后跟。* End)

2)由于Matcher方法调用返回的结果的含义(不一定直观),第二种模式给出的组计数为1,但开始和结束为-1 。

  • Matcher.find()如果找到匹配项,则返回true。在您的情况下,匹配是(?:Yes)模式的一部分。
  • Matcher.groupCount()返回捕获模式中捕获组的数量,而不管捕获组是否实际参与了比赛。在您的情况下,只有模式的非捕获(?:Yes)部分参与了比赛,但是捕获(.*)组仍然是模式的一部分,因此组数为1。
  • Matcher.start(n)Matcher.end(n)返回与第n个捕获组匹配的子序列的开始索引和结束索引。在您的情况下,尽管找到了总体匹配项,但(.*)捕获组未参与匹配,因此也未捕获子序列,因此结果为-1。

3)(在注释中提出问题。)为了确定实际上有多少个捕获组捕获了一个子序列,请Matcher.start(n)从0 迭代到Matcher.groupCount()计算非-1结果的数量。(请注意,这Matcher.start(0)是代表整个模式的捕获组,您可能出于目的将其排除在外。)