所有格泛型量词{m,n}+在Ruby 1.9.3中未实现



所有格量词贪婪,拒绝回溯。一个正则表达式/.{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 bug,还是有可能激发这种行为?也许,由于某种原因,Oniguruma实现了所有的量词?, *, +除了通用量词{m,n} ?如果是这样,为什么呢?

真实情况

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

工作

您可以使用更一般的构造非回溯组(或原子组)(?>pattern)来解决这个问题。让我们以一般情况pattern{n,m}+为例,构建具有非回溯组的等效regex(相当于Java与pattern{n,m}+匹配时的行为):

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

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

  • 当找到pattern的匹配(一个重复的实例)时,不允许在pattern中回溯。(注意,只要没有找到实例,就允许在pattern内回溯)。这是用内部非回溯组来模拟的。
  • 当无法找到pattern的实例时,不允许回溯以删除任何实例。这是在外部非回溯组中模拟的。

我不确定这里是否有任何警告。

如果您发现任何没有使用此方法模拟的情况,请给我留言。测试

测试1

首先,我测试了这个正则表达式:
(.{1,3}+)b

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

输入:

2343333ab

结果是整个字符串匹配,并且捕获组捕获2343333a(末尾没有b)。这表明上限不知何故被打破了。

DEMO at rubular

测试2

第二个测试揭示了范围量词{n}的行为如何不能被修改为所有格,这很可能也适用于其他范围量词{n,}{n,m}。相反,下面的+将只显示一个或多个时间行为的重复。

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

测试正则表达式:

(.{3}+)b
输入字符串

:

23d4344333ab
234344333ab
23434433ab

捕获组1捕获的匹配项都是3的倍数。从上到下,regex分别为输入字符串跳过2、1、0个字符。

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

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

DEMO at 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<"));
  }
}

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

最新更新