真正发生的事情
似乎+
后跟范围量词并没有为范围量词提供所有格属性。相反,它被视为前面重复一次或多次的任何内容。以.{1,3}+b
为例,它将等效于(?:.{1,3})+b
.
解决方法
您可以使用更通用的构造非回溯组(或原子分组)来解决此问题(?>pattern)
。让我们以一般情况pattern{n,m}+
为例,构造具有非回溯组的等效正则表达式(等效于 Java 与 匹配时的行为pattern{n,m}+
):
(?>(?>pattern){n,m})
为什么有 2 个级别的非回溯组?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 演示
测试代码以解决问题
这是 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<"));
}
}