ruby - 占有泛型量词 {m,n}+ 未在 Ruby 1.9.3 中实现?

标签 ruby regex oniguruma

所有格量词是贪婪的,拒绝回溯。正则表达式 /.{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}+ 为例,构造等价的带非回溯组的正则表达式(等价于 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}。相反,以下 + 只会表现出重复 1 次或多次行为。

(我初步的结论是+覆盖了上限,结果证明是错误的)。

测试正则表达式:

(.{3}+)b

输入字符串:

23d4344333ab
234344333ab
23434433ab

捕获组1中捕获的匹配项都是3的倍数。从上到下,正则表达式分别为输入字符串跳过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<"));
  }
}

关于ruby - 占有泛型量词 {m,n}+ 未在 Ruby 1.9.3 中实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15520273/

相关文章:

ruby-on-rails - 在这种情况下使用 Redis 合适吗?

javascript - 如何格式化包含变量的正则表达式?

python - 取行中不带标签的数值,正则表达式

ruby - Oniguruma 正则表达式引擎中的命名子程序?

regex - 用于捕获表情符号所有肤色变化的正则表达式

ruby - 如何在不使用 Ruby 保存到磁盘的情况下生成 zip 文件?

ruby - 如何在 Ruby 的数组中组合子数组?

ruby-on-rails - Ruby/Rails - 链接未知数量的方法调用

php - 确定任何一个字符是否在一行中至少重复了指定次数

ruby - 等同于 Vim 的正则表达式开始和结束匹配 (`\zs` 和 `\ze` )?