【问题标题】:Ruby regex multiple repeating capturesRuby 正则表达式多次重复捕获
【发布时间】:2018-03-12 04:25:18
【问题描述】:

我试图用正则表达式解析网页的一个子集,只是为了好玩。在我遇到以下问题之前,这很有趣。我有如下一段;

foo: 1, 2, 3, 4 and 5.
bar: 1, 2 and 3.

我想要做的是,通过应用以下正则表达式来获取以foo: 开头的段落第一行中的数字:

foo:(?:\s(\d)(?:,|\sand|\.))+

这与上面的字符串匹配,但它只捕获捕获组的最后一次出现,即5

如何使用单个正则表达式模式捕获以foo: 开头的段落中的所有数字,直到第一次出现.

【问题讨论】:

  • 如果没有\G rubular.com/r/VKOaLEYmSI 的帮助,您将无法使用Ruby
  • 是的,但不需要使用\Ks.scan(/(?:foo:\s*|(?!\A)\G\s*(?:,|and)?\s*)(\d+)/) 就可以了。但是s[/foo:([^.]+)/,1].scan(/\d+/) 看起来更漂亮。
  • 虽然它不影响 Rubular 显示结果的方式,但 \K 用于纯粹的数字表示。
  • \G 成功了,谢谢,但您能否将其作为答案发布,并附上\G 的解释以及\K 的解释?我已经进行了搜索并找到了它的作用,但解释性答案可能会帮助正在寻找类似问题答案的人。
  • 谁是“您的解决方案”的所有者?请以您要发表评论的人的用户名开始您的 cmets,并以“@”开头。看看其他 SO 问题,你就会明白我的意思了。

标签: ruby regex


【解决方案1】:

在大多数编程语言中,重复捕获组的数据不会单独存储,因此您不能单独引用它们。这是使用\G 锚的正当理由。 \G 导致匹配从上一个匹配结束的地方开始,或者它将匹配字符串的开头,与 \A 相同。

所以我们需要它的第一个功能:

(?:foo:|\G(?!\A))\s*(\d+)\s*(?:,|and)?

细分:

  • (?:启动非捕获组
    • foo:匹配foo:
    • |或者
    • \G(?!\A) 从上一场比赛结束的地方继续比赛
  • ) NCG 结束
  • \s* 任意数量的空白字符
  • (\d+) 匹配和捕获数字
  • \s* 任意数量的空白字符
  • (?:,|and)? 可选,and

此正则表达式将在输入字符串中遇到 foo 时开始匹配。然后尝试查找逗号或and 之前的下一个数字(数字周围允许空格)。

\K 令牌将重置匹配。这意味着它将向引擎发送一个信号以忘记到目前为止匹配的内容(但保留捕获的内容),然后将光标留在该位置。

我在 Rubular 正则表达式中使用 \K 来使结果集没有匹配的字符串而是捕获的数字。然而,Rubular 似乎工作方式不同,不需要\K。这根本不是必须的。

【讨论】:

    【解决方案2】:

    这个答案只使用了一个正则表达式,但无可否认地做了一些预处理和后处理。 (请允许我一点乐趣。我确实认为这里可能有一些指导价值。)

    str = "foo: 1, 2, 34, 4 and 5. and 6."
    
    r = /
        \d+             # match one or more digits
        (?=[^.]+:oof\z) # match one or more digits other than a period, followed
                        # by ":oof" at the end of the string, in a positive lookahead
        /x              # free-spacing regex definition mode
    
    str.reverse.scan(r).join(' ').reverse.split
      #=> ["1", "2", "34", "4", "5"]
    

    步骤如下。

    s = str.reverse
      #=> ".6 dna .5 dna 4 ,43 ,2 ,1 :oof"
    a  = s.scan r
      #=> ["5", "4", "43", "2", "1"]
    b  = a.join(' ')
      #=> "5 4 43 2 1"
    c  = b.reverse
      #=> "1 2 34 4 5"
    c.split
      #=> ["1", "2", "34", "4", "5"]
    

    如果没有匹配则返回一个空数组。

    那么,为什么要反转呢?这是为了让我可以使用正向 lookahead,它与正向 lookbehind 不同,它允许可变长度匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-13
      • 2011-03-11
      • 2017-01-07
      • 2019-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多