正则表达式的命名分组是一种为匹配的子表达式赋予有意义名称的方式,以便更清晰和方便地引用和处理匹配结果。
在传统的正则表达式中,我们使用括号 () 来创建分组,通过数字来引用这些分组。但在命名分组中,我们可以为每个分组指定一个名称,然后通过这个名称来访问匹配的内容。
正则表达式的命名分组语法通常采用以下形式:
(?<name>pattern)
其中,?<name> 是命名分组的标识符,name 是你为分组指定的名称,它必须是一个合法的标识符。pattern 则是要匹配的具体正则表达式模式。
例如:
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}
上述示例中,?<year> 、 ?<month> 和 ?<day> 就是命名分组,我们可以通过 year、month 和 day 名称访问这些分组。
命名分组的优点在于:
(1)提高代码的可读性:通过有意义的名称,而不是数字索引,能够更直观地理解匹配的部分代表什么。
(2)增强代码的可维护性:当正则表达式变得复杂,有多个分组时,名称可以减少混淆和出错的可能性。
Java7 引入了命名捕获组。例如,如下是一个有效的正则表达式:
(?<city>[\p{L)]+),\s*(?<state>[A-Z](2})
在 Java8 中,你可以在 Matcher 类的 start、end 和 group 方法中使用分组名称,例如:
String date = "我的出生在 2020-10-22,你呢?"; Pattern pattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})"); Matcher matcher = pattern.matcher(date); if (matcher.find()) { System.out.println("year=" + matcher.group("year")); System.out.println("month=" + matcher.group("month")); System.out.println("day=" + matcher.group("day")); //输出: //year=2020 //month=10 //day=22 int start = matcher.start("year"); int end = matcher.end("day"); System.out.println("start=" + start + ", end=" + end); //start=6, end=16 }
Pattern 类增加了一个 splitAsStream 方法,可以将一个 CharSequence 按照正则表达式进行分隔,方法定义如下:
public Stream<String> splitAsStream(CharSequence input)
从给定的输入序列中创建一个与此模式匹配的流。
此方法返回的数据流包含输入序列中的每个子串,这些子串由另一个与此模式匹配的子序列终止,或由输入序列的末尾终止。数据流中的子串按照它们在输入中出现的顺序排列。尾部为空的字符串将被丢弃,不会出现在数据流中。
如果该模式不匹配输入的任何子序列,那么产生的数据流只有一个元素,即字符串形式的输入序列。
如果输入序列的开头存在正宽度匹配,那么流的开头就会包含一个空的前导子串。然而,在开头的零宽度匹配绝不会产生这样的空前导子串。
如果输入序列是可变的,则在执行终端流操作期间必须保持不变。否则,终端流操作的结果将是未定义的。
示例:
String date = "2020-10-22"; Pattern pattern = Pattern.compile("-"); Stream<String> stream = pattern.splitAsStream(date); stream.forEach(System.out::println); //2020 //10 //22
方法 asPredicate 方法可以用来过滤匹配正则表达式的字符串,方法定义:
public Predicate<String> asPredicate()
创建一个可用于匹配字符串的谓词。
示例:
// 匹配所有首字母为答谢,长度为 3 或 4 的单词 Stream.of("Tome", "Bill", "Dick", "Harry") .filter(Pattern.compile("^[A-Z][a-z]{2,3}$").asPredicate()).forEach(System.out::println); //输出: //Tome //Bill //Dick