我有一个相当简单的正则表达式,但我想使用命名正则表达式来使其更清晰,然后迭代结果。
测试字符串:
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">.
MatchData 对象中没有 .
each
方法,我真的不想对其进行猴子修补。
如何解决这个问题?
我没有我想的那么清楚:重点是保留名称和类似散列的结构。
如果您需要完整的哈希值:
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
今天发布了新的 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
上一样。
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"
}
虽然正则表达式很强大,但它们的诱惑可能太诱人了,当有更简单或直接的方法来完成某件事时,我们就会陷入尝试使用它们的境地。这只是值得思考的事情。
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
将返回该值,以及包含键的数组的大小等。
@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
选项可用于
MatchData#named_captures
如果关键字参数 symbolize_names
被赋予真值,则结果哈希中的键是 Symbols
m = "12".match(/(?<a>.)(?<b>.)(?<c>.)?/) #=> #<MatchData "12" a:"1" b:"2" c:nil>
m.named_captures #=> {"a" => "1", "b" => "2", "c" => nil}
m.named_captures(symbolize_names: true) #=> {:a => "1", :b => "2", :c => nil}
new_dimensions = {}
dimensions.names.each { |k| new_dimensions[k] = my_operation(dimensions[k]) }