20

我有一个相当简单的正则表达式,但我想使用命名的正则表达式使其更清晰,然后迭代结果。

测试字符串:

testing_string = "111x222b333"

我的正则表达式:

regexp = %r{
                (?<width> [0-9]{3} ) {0}
                (?<height> [0-9]{3} ) {0}
                (?<depth> [0-9]+ ) {0}

                \g<width>x\g<height>b\g<depth>
            }x
dimensions = regexp.match(testing_string)

这项工作就像一个魅力,但问题出在此处:

dimensions.each { |k, v| dimensions[k] = my_operation(v) }

# ERROR !

 undefined method `each' for #<MatchData "111x222b333" width:"111" height:"222" depth:"333">.

没有。eachMatchData 对象中的方法,我真的不想修补它。

我该如何解决这个问题?

我并没有我想的那么清楚:关键是要保留名称和类似哈希的结构。

4

5 回答 5

38

如果你需要一个完整的哈希:

captures = Hash[ dimensions.names.zip( dimensions.captures ) ]
p captures
#=> {"width"=>"111", "height"=>"222", "depth"=>"333"}

如果您只想遍历名称/值对:

dimensions.names.each do |name|
  value = dimensions[name]
  puts "%6s -> %s" % [ name, value ]
end
#=>  width -> 111
#=> height -> 222
#=>  depth -> 333

备择方案:

dimensions.names.zip( dimensions.captures ).each do |name,value|
  # ...
end

[ dimensions.names, dimensions.captures ].transpose.each do |name,value|
  # ...
end

dimensions.names.each.with_index do |name,i|
  value = dimensions.captures[i]
  # ...
end
于 2012-07-27T15:06:42.240 回答
9

所以今天发布了一个新的 Ruby 版本(2.4.0)其中包括许多新功能,其中包括功能 #11999,又名MatchData#named_captures。这意味着您现在可以这样做:

h = '12'.match(/(?<a>.)(?<b>.)(?<c>.)?/).named_captures
#=> {"a"=>"1", "b"=>"2", "c"=>nil}
h.class
#=> Hash

所以在你的代码更改中

dimensions = regexp.match(testing_string)

dimensions = regexp.match(testing_string).named_captures

您也可以在正则表达式匹配结果上使用该each方法,就像在任何其他上一样Hash

于 2016-12-25T10:47:02.920 回答
1

我会以不同的方式解决创建哈希的整个问题:

irb(main):052:0> testing_string = "111x222b333"
"111x222b333"
irb(main):053:0> hash = Hash[%w[width height depth].zip(testing_string.scan(/\d+/))]
{
    "width" => "111",
    "height" => "222",
    "depth" => "333"
}

虽然正则表达式很强大,但它们的警笛声可能太诱人了,当有更简单或直接的完成某事的方法时,我们会被吸引尝试使用它们。这只是需要考虑的事情。


要跟踪扫描的元素数量,请根据 OP 评论:

hash = Hash[%w[width height depth].zip(scan_result = testing_string.scan(/\d+/))]
=> {"width"=>"111", "height"=>"222", "depth"=>"333"}
scan_result.size
=> 3

hash.size将返回它,包含键的数组的大小等也会返回。

于 2012-07-27T16:22:40.610 回答
1

如果您的所有捕获都有唯一的名称, @Phrogz 的答案是正确的,但是您可以为多个捕获提供相同的名称。这是 Regexp 文档中的一个示例。

此代码支持具有重复名称的捕获:

captures = Hash[
  dimensions.regexp.named_captures.map do |name, indexes|
    [
      name,
      indexes.map { |i| dimensions.captures[i - 1] }
    ]
  end
]

# Iterate over the captures
captures.each do |name, values|
  # name is a String
  # values is an Array of Strings
end
于 2015-11-20T23:19:03.730 回答
-1

如果你想保留名字,你可以这样做

new_dimensions = {}
dimensions.names.each { |k| new_dimensions[k] = my_operation(dimensions[k]) }
于 2012-07-27T13:41:33.287 回答