占有量词是贪婪的,拒绝回溯。正则表达式/.{1,3}+b/
应该表示:匹配除换行符之外的任何字符,尽可能多地匹配1到3次,并且不要回溯。然后匹配角色b
。
在这个例子中:
'ab'.sub /.{1,3}+b/, 'c' #=> "c"
不应该进行替换,与事实相反。
这两个例子的结果不同:
'aab'.sub /.{0,1}+b/, 'c' #=> "c"
'aab'.sub /.?+b/, 'c' #=> "ac"
将此与Scala进行比较,他们给出了相同的答案:
scala> ".{0,1}+b".r.replaceAllIn("aab", "c")
res1: String = ac
scala> ".?+b".r.replaceAllIn("aab", "c")
res2: String = ac
这是一个Ruby错误,还是可以激发这种行为?也许,Oniguruma出于某种原因对除了通用量词?
之外的所有量词*
,+
,{m,n}
实施了所有权?如果是这样,为什么?
答案 0 :(得分:5)
似乎+
后跟范围量词不会为范围量词提供所有格属性。相反,它被视为前面重复一次或多次。以.{1,3}+b
为例,它等同于(?:.{1,3})+b
。
您可以使用更通用的构造非回溯组(或原子分组)(?>pattern)
解决此问题。让我们使用一般情况pattern{n,m}+
作为示例来构造具有非回溯组的等效正则表达式(相当于与pattern{n,m}+
匹配时的Java行为):
(?>(?>pattern){n,m})
为什么有两个级别的非回溯组? 2是必要的,因为:
pattern
(一个重复实例)的匹配项,则不允许在pattern
内进行回溯。 (请注意,只要未找到实例,就允许在pattern
内进行回溯)。这是使用内部非回溯组模拟的。pattern
的更多实例时,不允许回溯以删除任何实例。这可以通过外部非回溯组来模拟。我不确定这里是否有任何警告。如果您发现任何未使用此方法模拟的情况,请与我联系。
起初,我测试了这个正则表达式:
(.{1,3}+)b
最初,我在没有捕获组的情况下进行了测试,但结果非常令人惊讶,我需要捕获组来确认发生了什么。
在此输入上:
2343333ab
结果是整个字符串匹配,捕获组捕获了2343333a
(最后没有b
)。这表明上限已经在某种程度上被打破了。
第二个测试揭示范围量词{n}
的行为如何不能被修改为占有,并且这也可能适用于其他范围量词{n,}
和{n,m}
。相反,以下+
只会重复出现一次或多次时间行为。
(我的初步结论是+
会覆盖上限,但事实证明这是错误的。)
测试正则表达式:
(.{3}+)b
输入字符串:
23d4344333ab
234344333ab
23434433ab
捕获组1中捕获的匹配都是3的倍数。从上到下,正则表达式分别为输入字符串跳过2,1,0个字符。
带注释的输入字符串([]
表示整个正则表达式的匹配,()
表示通过捕获组1捕获的文本):
23[(d4344333a)b]
2[(34344333a)b]
[(23434433a)b]
这是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<"));
}
}
答案 1 :(得分:2)
似乎这是在Oniguruma。 Documentation说{n,m}+, {n,}+, {n}+ are possessive op. in ONIG_SYNTAX_JAVA only
。我想这是因为向后兼容的原因,或者?