'gsub' 中的默认值

Default value in 'gsub'

我有一个如下所示的数组:

arr = [
  "---\n",
  ":date: 2018-07-31\n  :story_points: 4.0\n  :remaining_hours: 4.0\n ",
  ":date: 2018-08-01\n  :story_points:    \n  :remaining_hours: 4.0\n ",
  ":date: 2018-08-22\n  :story_points: 8.0\n  :remaining_hours: 0.0\n "
]

我想在各自的数组中提取 datestory_points 的值。如果缺少 story_points 的值,则应提供 "0.0" 的默认值。输出应如下所示:

["2018-07-31", "2018-08-01", "2018-08-22"]
["4.0", "0.0", "8.0"]

我已经按照另一位用户在我之前 post 中的建议尝试了以下方法:

arr.join.gsub(/(?<=:date: )\d{4}-\d{2}-\d{2}/).to_a
arr.join.gsub(/(?<=:story_points: )\d{1}.\d{1}/).to_a 

以上将给出:

["2018-07-31", "2018-08-01", "2018-08-22"]
["4.0", "8.0"]

我无法获取默认值。有人可以帮忙吗?

试试下面的代码:

arr[1..-1].map { |s| (s.match(/(?<=:story_points: )\d{1}.\d{1}/) || '0.0').to_s }
#=> ["4.0", "0.0", "8.0"]

基于数组单次传递的基于正则表达式的替代解决方案:

arr = [
  "---\n",
  ":date: 2018-07-31\n  :story_points: 4.0\n  :remaining_hours: 4.0\n ",
  ":date: 2018-08-01\n  :story_points:    \n  :remaining_hours: 4.0\n ",
  ":date: 2018-08-22\n  :story_points: 8.0\n  :remaining_hours: 0.0\n "
]
dates = []
sp = []
rx = /:date:\s*(\d{4}-\d{2}-\d{2})\s*:story_points:\s*(\d+\.\d+)?/
arr.each { |x| x.scan(rx) { |m,n| dates << m; sp << (n || "0.0")  } }
# => dates: [ "2018-07-31", "2018-08-01", "2018-08-22" ]
# => sp: [ "4.0", "0.0", "8.0" ]

Ruby demo

这是Rubular demo

图案解释

  • :date: - 文字 :date: 子串
  • \s* - 0+ 个空格
  • (\d{4}-\d{2}-\d{2}) - 捕获组 1:类似日期的模式
  • \s* - 0+ 个空格
  • :story_points: - 文字 :story_points: 子串
  • \s* - 0+ 个空格
  • (\d+\.\d+)? - 捕获组 2(可选,由于 ?):1+ 位,. 和 1+ 位。
r = /
    \A                  # match beginning of string
    :date:              # match string
    [ ]+                # match one or more spaces
    (\d{4}-\d{2}-\d{2}) # match string in capture group 1
    \n                  # match newline
    [ ]+                # match one or more spaces
    :story_points:      # match string
    [ ]+                # match one or more paces
    (                   # begin capture group 2
      \d+\.\d+          # match a non-negative float 
      |                 # or
      [ ]+              # match one or more spaces
    )                   # end capture group 2
    /x                  # free-spacing regex definition mode

arr.each_with_object([]) do |s,a|
  res = s.scan(r).flatten
  a << res unless res.empty?
end.transpose.tap { |a| a[1].map! { |s| s.to_f.to_s } }
  #=> [["2018-07-31", "2018-08-01", "2018-08-22"], ["4.0", "0.0", "8.0"]]

常规形式的正则表达式如下。

    r = /\A:date: +(\d{4}-\d{2}-\d{2})\n +:story_points: +(\d+\.\d+| +)/

字符 类 外的空格在使用自由间距模式时被删除,这就是为什么我在正则表达式的常规形式中将 [ ] 替换为空格。 / +\n/ 可以替换为 /\s+/,但允许使用制表符、其他空白字符、无空格和多个换行符,这可能是不需要的。

注意下面的中间计算。

arr.each_with_object([]) do |s,a|
  res = s.scan(r).flatten
  a << res unless res.empty?
end
  #=> [["2018-07-31", "4.0"], ["2018-08-01", " "], ["2018-08-22", "8.0"]]

我用 Object#tap 代替了以下内容。

a = arr.each_with_object([]) do |s,a|
  res = s.scan(r).flatten
  a << res unless res.empty?
end.transpose
a[1].map! { |s| s.to_f.to_s }
a