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

拥有量词是贪婪的,拒绝回溯。 正则表达式/.{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} ? 如果是这样的话,为什么?


真的发生了什么

似乎+范围量词并没有提供范围量词的所有格属性。 相反,它被视为在前面重复一次或多次。 以.{1,3}+b为例,它将等同于(?:.{1,3})+b

变通

你可以用更一般的构造非回溯组(或原子分组)来解决这个问题(?>pattern) 。 让我们用一般案例pattern{n,m}+作为例子来构造具有非回溯组的等效正则表达式(等价于与pattern{n,m}+匹配时的Java行为):

(?>(?>pattern){n,m})

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

  • 当发现pattern匹配(重复的一个实例)时, pattern内的回溯将被禁止。 (请注意,只要没有找到实例,就允许在pattern内回溯)。 这与内部非回溯组仿真。
  • 当不能找到更多的pattern实例时,不允许回溯移除任何实例。 这与外部非回溯组仿效。
  • 我不确定这里是否有任何警告。 如果您发现任何情况不是使用此方法进行模拟,请对我发表评论。

    测试

    测试1

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

    (.{1,3}+)b
    

    最初,我在没有捕获组的情况下进行了测试,但结果令人惊讶,我需要捕获组来确认发生了什么。

    在这个输入上:

    2343333ab
    

    结果是整个字符串匹配 ,捕获组捕获2343333a (最后没有b )。 这表明上限已被打破。

    在ruby中进行演示

    测试2

    这第二个测试揭示范围量词{n}的行为不能被修改为占有性,并且很可能这也适用于其他范围量词{n,}{n,m} 。 相反,以下的+只会表现出一次或多次时间行为的重复。

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

    测试正则表达式:

    (.{3}+)b
    

    输入字符串:

    23d4344333ab
    234344333ab
    23434433ab
    

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

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

    23[(d4344333a)b]
    2[(34344333a)b]
    [(23434433a)b]
    

    在ruby中进行演示

    测试代码以解决问题

    这是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<"));
      }
    }
    

    这似乎是在Oniguruma中打算的。 文档说{n,m}+, {n,}+, {n}+ are possessive op. in ONIG_SYNTAX_JAVA only {n,m}+, {n,}+, {n}+ are possessive op. in ONIG_SYNTAX_JAVA only 。 我猜这是因为向后兼容的原因,还是?

    链接地址: http://www.djcxy.com/p/76983.html

    上一篇: Possessive generic quantifier {m,n}+ not implemented in Ruby 1.9.3?

    下一篇: match and lazy quantifier with strange behavior