【问题标题】:Java Matcher groups: Understanding The difference between "(?:X|Y)" and "(?:X)|(?:Y)"Java Matcher 组:了解“(?:X|Y)”和“(?:X)|(?:Y)”之间的区别
【发布时间】:2011-02-27 23:08:22
【问题描述】:

谁能解释一下:

  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

【问题讨论】:

    标签: java regex regex-group


    【解决方案1】:
    1. 不同之处在于,在第二个模式"(?:Yes)|(?:No)(.*)End" 中,连接(“XY”中的“X 后跟 Y”)比选择(“X|Y”中的“X 或 Y”)具有更高的precedence ),就像乘法的优先级高于加法一样,所以模式等价于

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

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

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

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

      在您的测试中,第二个模式的组 1 位于(空)范围 [-1, -1[,因为该组不匹配(包括开始 -1,不包括结束 -1,使 half-open interval 为空)。

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

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

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

      explains:

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

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

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

    5. 谈到反向引用时,还要注意the regex tutorial 所说的:

      反向引用与 一个不匹配的捕获组,一个 一个完全没有参加比赛的捕获组。

    【讨论】:

    • 感谢您的回答。我仍然想了解为什么组数为 1。我理解(从文档和其他实验中)组数为 1 应该意味着已找到单个编号组,因此 start(1) 应该 > - 1.
    • 组数纯粹是通过计算分组括号获得的,而您的模式正好有一个:(.*)。这是第 1 组。任何特定组是否与给定输入匹配,与该定义无关。例如,在模式"(Yes)|(No)" 中,有两个组(“(Yes)” 是第 1 组,“(No)” 是第 2 组),但对于任何给定的输入,只有其中一个可以匹配。
    • 所以您说的是文档中所说的“返回此匹配器模式中捕获组的数量”。这意味着即使没有匹配项,表达式中的计数?在那种情况下,为什么调用 find() 会返回 true?或者换句话说,如何确定是否有任何组匹配,如果匹配,有多少?
    【解决方案2】:

    总结一下,

    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) 是代表整个模式的捕获组,您可能需要将其排除在外。)

    【讨论】:

      【解决方案3】:

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

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

      你想要的是

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

      【讨论】:

      • 您对 groupCount 的看法是错误的,正如 Javadoc 清楚地解释的那样:按照惯例,组零表示整个模式。它包含在此计数中。不直观。
      【解决方案4】:

      当使用正则表达式时,重要的是要记住有一个隐含的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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-31
        • 2012-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多