7

谁能解释一下:

  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 );
  }

 }

这给出了以下输出:

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
4

4 回答 4

9
  1. 不同之处在于,在第二种模式"(?:Yes)|(?:No)(.*)End"中,连接(“XY”中的“X 后跟 Y”)比选择(“X|Y”中的“X 或 Y”)具有更高的优先级,就像乘法的优先级高于另外,所以模式等价于

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

    您想要得到的是以下模式:

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

    这会产生与您的第一个模式相同的输出。

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

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

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

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

    解释

    以 开头的组(?要么是不捕获文本且不计入组总数的纯非捕获组,要么是命名捕获组。

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

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

  5. 当谈到反向引用时,还要注意正则表达式教程必须说的内容:

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

于 2010-06-04T20:09:54.783 回答
5

总结一下,

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

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

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

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

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

于 2010-06-07T11:36:06.210 回答
3

由于“|”的优先级 模式中的运算符,第二个模式等价于:

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

你想要的是

(?:(?:Yes)|(?:No))(.*)End
于 2010-06-04T20:13:42.170 回答
1

使用正则表达式时,重要的是要记住有一个隐式AND运算符在起作用。这可以从java.util.regex.Pattern涵盖逻辑运算符的 JavaDoc 中看出:

逻辑运算符
XY X 后跟 Y
X|Y X 或 Y
(X) X,作为捕获组

AND优先OR于第二个模式中的。第二个 Pattern 等价于
(?:Yes)|(?:(?:No)(.*)End).
为了使其与第一个 Pattern 等效,必须将其更改为
(?:(?:Yes)|(?:No))(.*)End

于 2010-06-04T20:27:06.723 回答