拥有通用量词{m,n} +没有在Ruby 1.9.3中实现?

Sta*_*erg 7 ruby regex oniguruma

占有量词是贪婪的,拒绝回溯.正则表达式/.{1,3}+b/应该是:匹配除换行符之外的任何字符,尽可能多地匹配1到3次,并且不要回溯.然后匹配角色b.

在这个例子中:

'ab'.sub /.{1,3}+b/, 'c'    #=> "c"
Run Code Online (Sandbox Code Playgroud)

与事实相反,不应该进行替换.

这两个例子的结果有所不同:

'aab'.sub /.{0,1}+b/, 'c'   #=> "c"
'aab'.sub /.?+b/, 'c'       #=> "ac"
Run Code Online (Sandbox Code Playgroud)

与Scala相比,他们给出了相同的答案:

scala> ".{0,1}+b".r.replaceAllIn("aab", "c")
res1: String = ac
scala> ".?+b".r.replaceAllIn("aab", "c")
res2: String = ac
Run Code Online (Sandbox Code Playgroud)

这是一个Ruby错误,还是可以激发这种行为?也许,Oniguruma出于某种原因实现占有与所有量词?,*,+除了一般的量词{m,n}?如果是这样的话,为什么?

nha*_*tdh 5

真正发生了什么

似乎+随后范围量词不向范围量词提供所有格属性.相反,它被视为前面重复一次或多次.使用它.{1,3}+b作为一个例子,它将等同于(?:.{1,3})+b.

变通

您可以使用更通用的构造非回溯组(或原子分组)解决此问题(?>pattern).让我们以一般情况pattern{n,m}+为例,构造具有非回溯组的等效正则表达式(相当于Java匹配时的行为pattern{n,m}+):

(?>(?>pattern){n,m})
Run Code Online (Sandbox Code Playgroud)

为什么2级非回溯组?2是必要的,因为:

  • 当找到匹配pattern(一个重复实例)时,pattern不允许回溯.(请注意,只要未找到实例,pattern就允许在内部进行回溯).这是使用内部非回溯组模拟的.
  • 如果找不到更多实例,pattern则不允许回溯以删除任何实例.这是用外部非回溯组模拟的.

我不确定这里是否有任何警告.如果您发现任何未使用此方法模拟的情况,请发表评论.

测试

测试1

起初,我测试了这个正则表达式:

(.{1,3}+)b
Run Code Online (Sandbox Code Playgroud)

最初,我测试没有捕获组,但结果是如此令人惊讶,我需要捕获组来确认发生了什么.

在此输入:

2343333ab
Run Code Online (Sandbox Code Playgroud)

结果是整个字符串匹配,并捕获捕获组2343333a(没有b结束).这表明上限已经以某种方式被打破.

在rubular的DEMO

测试2

第二个测试揭示了范围量词{n}的行为如何不能被修改为占有欲,并且很可能这也适用于其他范围量词{n,}{n,m}.相反,以下+内容仅表示重复一次或多次时间行为.

(我的初步结论是+覆盖了上限,但事实证明这是错误的).

测试正则表达式:

(.{3}+)b
Run Code Online (Sandbox Code Playgroud)

输入字符串:

23d4344333ab
234344333ab
23434433ab
Run Code Online (Sandbox Code Playgroud)

在捕获组1中捕获的匹配都是3的倍数.从上到下,正则表达式分别跳过输入字符串的2,1,0个字符.

带注释的输入字符串([]表示整个正则表达式的匹配,()表示捕获组1捕获的文本):

23[(d4344333a)b]
2[(34344333a)b]
[(23434433a)b]
Run Code Online (Sandbox Code Playgroud)

在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<"));
  }
}
Run Code Online (Sandbox Code Playgroud)


Sta*_*erg 2

这似乎是 Oniguruma 的意图。文档{n,m}+, {n,}+, {n}+ are possessive op. in ONIG_SYNTAX_JAVA only。我想这是因为向后兼容性的原因,或者?