真正发生的事情
看起来,加号后跟范围量词不会为范围量词提供“占有”属性。相反,它被视为前面的任何内容重复一次或多次。以.{1,3}+b
为例,它将等同于(?:.{1,3})+b
。
解决方法
您可以使用更通用的构造非回溯组(或原子分组)(?>pattern)
来解决这个问题。让我们以一般情况 pattern{n,m}+
为例,构造与非回溯组等效的正则表达式(相当于在 Java 中使用 pattern{n,m}+
进行匹配时的行为):
(?>(?>pattern){n,m})
为什么需要两个级别的非回溯组? 需要2个原因:
- 当找到
pattern
的匹配项时(一个重复实例),在pattern
内部进行回溯是不允许的。 (请注意,只要未找到实例,就可以在pattern
中进行回溯)。这是使用内部非回溯组模拟的。
- 当不能再找到
pattern
的其他实例时,禁止回溯以删除任何实例。 这是使用外部非回溯组模拟的。
我不确定是否有任何警告。 如果您发现任何未使用此方法模拟的情况,请用评论提醒我。
测试
测试1
首先,我测试了这个正则表达式:
(.{1,3}+)b
一开始我测试时没有使用捕获组,但结果出乎意料,所以我需要使用捕获组来确认发生了什么。
在这个输入上:
2343333ab
结果是
整个字符串匹配,捕获组捕获了
2343333a
(末尾没有
b
)。这表明上限已被突破。
rubular演示
测试2
第二个测试显示了范围量词{n}
的行为不能被修改为贪婪模式,而且很可能对其他范围量词{n,}
和{n,m}
也适用。相反,下面的+
将只表现出重复1次或更多次的行为。
(我的初步结论是+
会覆盖上限,但事实证明我是错的)。
测试正则表达式:
(.{3}+)b
输入字符串:
23d4344333ab
234344333ab
23434433ab
捕获组1中捕获的匹配项都是3的倍数。从上到下,正则表达式分别跳过输入字符串的2、1、0个字符。
带注释的输入字符串([]
表示整个正则表达式的匹配项,()
表示捕获组1捕获的文本):
23[(d4344333a)b]
2[(34344333a)b]
[(23434433a)b]
在 rubular 上尝试 DEMO
解决问题的测试代码
这是 Java 中的测试代码,以显示内部和外部不回溯组均为必要条件。 ideone
class TestPossessive {
public static void main(String args[]) {
String inputText = "123456789012";
System.out.println("Input string: " + inputText);
System.out.println("Expected: " + inputText.replaceFirst("(?:\\d{3,4}(?![89])){2,}+", ">$0<"));
System.out.println("Outer possessive group: " + inputText.replaceFirst("(?>(?:\\d{3,4}(?![89])){2,})", ">$0<"));
System.out.println("Inner possessive group: " + inputText.replaceFirst("(?>\\d{3,4}(?![89])){2,}", ">$0<"));
System.out.println("Both: " + inputText.replaceFirst("(?>(?>\\d{3,4}(?![89])){2,})", ">$0<"));
System.out.println();
inputText = "aab";
System.out.println("Input string: " + inputText);
System.out.println("Expected: " + inputText.replaceFirst(".{1,3}+b", ">$0<"));
System.out.println("Outer possessive group: " + inputText.replaceFirst("(?>.{1,3})b", ">$0<"));
System.out.println("Inner possessive group: " + inputText.replaceFirst("(?>.){1,3}b", ">$0<"));
System.out.println("Both: " + inputText.replaceFirst("(?>(?>.){1,3})b", ">$0<"));
}
}