【问题标题】:Capturing groups don't work as expected with Ruby scan method使用 Ruby 扫描方法捕获组无法按预期工作
【发布时间】:2015-07-09 13:32:28
【问题描述】:

我需要从多行字符串中获取浮点数数组(正数和负数)。例如:-45.124, 1124.325

这是我的工作:

text.scan(/(\+|\-)?\d+(\.\d+)?/)

虽然它在regex101 上运行良好(捕获组 0 匹配我需要的所有内容),但它在 Ruby 代码中不起作用。

任何想法为什么会发生以及我可以如何改进?

【问题讨论】:

  • 另一个很棒的网站是Rubular
  • @onebree 感谢您的链接!我肯定会将它用于 ruby​​ 正则表达式

标签: ruby regex


【解决方案1】:

scan documentation:

如果模式不包含任何组,则每个单独的结果都包含匹配的字符串$&如果模式包含组,则每个单独的结果本身就是一个数组,每个组包含一个条目。

您应该删除捕获组(如果它们是多余的),或者将它们设为non-capturing(如果您只需要对一系列模式进行分组以便能够量化它们),或者使用额外的代码/group 以防无法避免捕获组。

  1. 在这种情况下,捕获组用于量化模式序列,因此您只需将所有未转义的( 替换为将捕获组转换为非捕获组 (?:(这里只出现了一次):
text = " -45.124, 1124.325"
puts text.scan(/[+-]?\d+(?:\.\d+)?/)

demo,输出:

-45.124
1124.325

好吧,如果您还需要匹配像 .04 这样的浮点数,您可以使用 [+-]?\d*\.?\d+。见another demo

  1. 在某些情况下,您无法摆脱捕获组,例如当正则表达式包含对捕获组的反向引用时。在这种情况下,您可以 a) 声明一个变量来存储所有匹配项并将它们全部收集在 scan 块中,或者 b) 将整个模式与另一个捕获组括起来并映射结果以从每个匹配项中获取第一项, c) 您可以使用带有正则表达式的gsub 作为单个参数来返回一个枚举器,使用.to_a 来获取匹配数组:
text = "11234566666678"
# Variant a:
results = []
text.scan(/(\d)\1+/) { results << Regexp.last_match(0) }
p results                              # => ["11", "666666"]
# Variant b:
p text.scan(/((\d)\2+)/).map(&:first)  # => ["11", "666666"]
# Variant c:
p text.gsub(/(\d)\1+/).to_a  # => ["11", "666666"]

this Ruby demo

【讨论】:

  • 这真是令人大开眼界。谢谢
  • 在您的情况 2 中,另一个选项是 "11234566666678".gsub(/(\d)\1+/).to_a #=&gt; ["11", "666666"]
  • @CarySwoveland 越多越好。虽然问题是关于.scan,但其他实现相同目标的方法真的很受欢迎。
【解决方案2】:
([+-]?\d+\.\d+)

假设小数点前有一个前导数字

demo at Rubular

【讨论】:

  • 这个正则表达式有一个缺点:它也匹配4.
  • @stribizhev 很好发现。更新
  • 不,现在不匹配1
【解决方案3】:

如果您需要捕获组来进行复杂的模式匹配,但又想要.scan 返回的整个表达式,这可以为您工作。

假设您想从带有 html 图片标签的 markdown 文本中获取此字符串中的图片网址:

str = %(
Before
<img src="https://images.zenhubusercontent.com/11223344e051aa2c30577d9d17/110459e6-915b-47cd-9d2c-1842z4b73d71">

After
<img src="https://user-images.githubusercontent.com/111222333/75255445-f59fb800-57af-11ea-9b7a-a235b84bf150.png">).strip

您可能定义了一个正则表达式以仅匹配 url,并且可能使用 Rubular example like this 来构建/测试您的 Regexp

image_regex = 
  /https\:\/\/(user-)?images.(githubusercontent|zenhubusercontent).com.*\b/

现在您不需要每个子捕获组,而只需要 .scan 中的整个表达式,您可以将整个模式包装在捕获组中并像这样使用它:

image_regex = 
  /(https\:\/\/(user-)?images.(githubusercontent|zenhubusercontent).com.*\b)/

str.scan(image_regex).map(&:first)
=> ["https://user-images.githubusercontent.com/1949900/75255445-f59fb800-57af-11ea-9b7a-e075f55bf150.png",
 "https://user-images.githubusercontent.com/1949900/75255473-02bca700-57b0-11ea-852a-58424698cfb0.png"]

这实际上是如何工作的?

由于您有 3 个捕获组,仅 .scan 将返回一个数组 Array,每个捕获一个:

str.scan(image_regex)
=> [["https://user-images.githubusercontent.com/111222333/75255445-f59fb800-57af-11ea-9b7a-e075f55bf150.png", "user-", "githubusercontent"],
 ["https://images.zenhubusercontent.com/11223344e051aa2c30577d9d17/110459e6-915b-47cd-9d2c-0714c8f76f68", nil, "zenhubusercontent"]]

由于我们只想要第一个(外部)捕获组,我们可以调用.map(&amp;:first)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多