ZetCode

Java Matcher.groupCount 方法

上次修改时间:2025 年 4 月 20 日

Matcher.groupCount 方法返回正则表达式模式中捕获组的数量。它是 java.util.regex 包的一部分。此方法对于在执行匹配之前确定模式包含多少个组非常有用。

捕获组是正则表达式模式中用括号括起来的部分。它们允许您提取匹配文本的特定部分。groupCount 方法不包括特殊的组 0,它总是表示整个匹配项。

基本定义

groupCount 方法在 Matcher 类中定义。它返回一个整数,表示模式中捕获组的数量。计数基于用于创建 Matcher 实例的模式。

方法签名很简单:public int groupCount。它不接受任何参数,并返回捕获组的计数。对于给定的 Matcher 实例,计数保持不变。

简单的组计数示例

这个基本示例演示了如何将 groupCount 与一个简单的模式一起使用。我们将创建一个具有两个捕获组的模式并检查计数。

SimpleGroupCount.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SimpleGroupCount {

    public static void main(String[] args) {
        String regex = "(\\d{3})-(\\d{3})"; // Two capturing groups
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("123-456");
        
        int groupCount = matcher.groupCount();
        System.out.println("Number of capturing groups: " + groupCount);
        
        if (matcher.matches()) {
            for (int i = 0; i <=  groupCount; i++) {
                System.out.println("Group " + i + ": " + matcher.group(i));
            }
        }
    }
}

在这个例子中,我们创建了一个模式,该模式匹配类似于电话号码的字符串,其中包含两组三位数字。 groupCount 方法返回 2,与我们的两个捕获组匹配。请注意,我们循环直到并包括 groupCount 以包括组 0(完全匹配)。

无捕获组示例

此示例显示了当模式不包含任何捕获组时 groupCount 的行为。在这种情况下,该方法将返回 0。

NoGroupsExample.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NoGroupsExample {

    public static void main(String[] args) {
        String regex = "\\d{3}-\\d{3}"; // No capturing groups
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("123-456");
        
        System.out.println("Number of capturing groups: " + 
            matcher.groupCount());
        
        if (matcher.matches()) {
            System.out.println("Full match: " + matcher.group(0));
            // matcher.group(1) would throw IndexOutOfBoundsException
        }
    }
}

在这里,模式匹配相同的电话号码格式,但没有捕获组。 groupCount 返回 0,并且尝试访问组 1 将会抛出一个异常。 只有组 0(完全匹配)可用。

嵌套组示例

此示例演示了 groupCount 如何与嵌套捕获组一起使用。 每对括号都计为一个单独的组,无论嵌套级别如何。

NestedGroups.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NestedGroups {

    public static void main(String[] args) {
        // One outer group containing two inner groups
        String regex = "((\\d{2})-(\\d{2}))-(\\d{4})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("12-31-2023");
        
        System.out.println("Number of capturing groups: " + 
            matcher.groupCount());
        
        if (matcher.matches()) {
            for (int i = 0; i <=  matcher.groupCount(); i++) {
                System.out.println("Group " + i + ": " + matcher.group(i));
            }
        }
    }
}

该模式有四个捕获组:整个日期部分,月-日部分,月份,日期和年份。 groupCount 返回 4。输出显示了每个嵌套组如何从左到右按顺序编号。

非捕获组示例

此示例说明了非捕获组(使用 (?:...) 语法)如何影响 groupCount。 非捕获组不包含在计数中。

NonCapturingGroups.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NonCapturingGroups {

    public static void main(String[] args) {
        // One capturing group and one non-capturing group
        String regex = "(\\d{3})(?:-)(\\d{3})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("123-456");
        
        System.out.println("Number of capturing groups: " + 
            matcher.groupCount());
        
        if (matcher.matches()) {
            for (int i = 0; i <=  matcher.groupCount(); i++) {
                System.out.println("Group " + i + ": " + matcher.group(i));
            }
        }
    }
}

该模式包含两个捕获组(用于数字)和一个非捕获组(用于连字符)。 groupCount 返回 2,忽略非捕获组。 连字符仍然是匹配的一部分,但未分配组号。

命名组示例

此示例显示了命名捕获组(使用 (?<name>...) 语法)如何与 groupCount 一起使用。 命名组像常规捕获组一样包含在计数中。

NamedGroupsExample.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NamedGroupsExample {

    public static void main(String[] args) {
        // Two named capturing groups
        String regex = "(?<area>\\d{3})-(?<exchange>\\d{3})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("123-456");
        
        System.out.println("Number of capturing groups: " + 
            matcher.groupCount());
        
        if (matcher.matches()) {
            System.out.println("Full match: " + matcher.group(0));
            System.out.println("Area code: " + matcher.group("area"));
            System.out.println("Exchange: " + matcher.group("exchange"));
            System.out.println("Group 1: " + matcher.group(1));
            System.out.println("Group 2: " + matcher.group(2));
        }
    }
}

该模式有两个命名组:“area”和“exchange”。 groupCount 返回 2,表明命名组被正常计数。 这些组可以通过它们的名称或它们的编号(在本例中为 1 和 2)进行访问。

复杂模式示例

此示例演示了 groupCount 与混合了不同组类型的更复杂的模式一起使用。 它显示了该方法在各种分组构造中的行为。

ComplexPattern.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ComplexPattern {

    public static void main(String[] args) {
        // Mix of capturing, non-capturing, and named groups
        String regex = "(\\d{2})(?:-)(?<month>\\d{2})-(\\d{4})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher("31-12-2023");
        
        System.out.println("Number of capturing groups: " + 
            matcher.groupCount());
        
        if (matcher.matches()) {
            System.out.println("\nAll groups:");
            for (int i = 0; i <=  matcher.groupCount(); i++) {
                System.out.println("Group " + i + ": " + matcher.group(i));
            }
            
            System.out.println("\nNamed group:");
            System.out.println("Month: " + matcher.group("month"));
        }
    }
}

该模式包含三个组:一个用于日的编号组,一个用于连字符的非捕获组,一个用于月份的命名组和一个用于年份的编号组。 groupCount 返回 3(非捕获组被排除)。 该示例显示了对编号组和命名组的访问。

真实示例:电子邮件解析

这个实际示例使用 groupCount 来解析电子邮件地址。 我们将提取电子邮件的不同部分,并演示如何处理这些组。

EmailParser.java
package com.zetcode;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EmailParser {
    
    public static void main(String[] args) {
        // Pattern to capture username, domain, and TLD separately
        String regex = "(\\w+)@([\\w.]+)\\.([a-z]{2,})";
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher("user@example.com");
        
        System.out.println("Email pattern has " + matcher.groupCount() + 
            " capturing groups");
            
        if (matcher.matches()) {
            System.out.println("\nEmail components:");
            System.out.println("Full address: " + matcher.group(0));
            System.out.println("Username: " + matcher.group(1));
            System.out.println("Domain: " + matcher.group(2));
            System.out.println("TLD: " + matcher.group(3));
            
            System.out.println("\nAll groups:");
            for (int i = 0; i <=  matcher.groupCount(); i++) {
                System.out.println("Group " + i + ": " + matcher.group(i));
            }
        }
    }
}

此模式将电子邮件地址分解为三个部分:用户名、域和顶级域 (TLD)。 groupCount 返回 3,用于这些捕获组。 该示例演示了如何单独访问每个部分以及如何以编程方式遍历所有组。

来源

Java Matcher.groupCount 文档

在本教程中,我们深入探讨了 Matcher.groupCount 方法。 我们已经看到了它如何与不同类型的组一起使用,以及在各种场景中。 了解此方法对于有效的正则表达式处理至关重要。

作者

我的名字是 Jan Bodnar,我是一位敬业的程序员,在该领域拥有多年的经验。 我于 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和八本电子书。 我拥有超过八年的教学经验,致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程