Ale*_*kin 3 ruby regex xml xpath nokogiri
我有简化时看起来像这样的XML:
node_set = Nokogiri::XML('
<PARENT>
<SOME_TAG>12:12:1222</SOME_TAG>
<HOLY_TAG>12:12:1222</HOLY_TAG>
<MAJOR_TAG>12:12:1222</MAJOR_TAG>
<FOO_FOO>12:12:1222</FOO_FOO>
</PARENT>'
)
Run Code Online (Sandbox Code Playgroud)
我所知道的只是如何为此写一个正则表达式:
(\d+):(\d+):(\d+)
Run Code Online (Sandbox Code Playgroud)
我在官方网站上阅读了一些关于正则表达式匹配的文章,但没有答案如何做到这一点.只有如何将用户函数调用到xpath方法的机制.
如果不通过regexp知道它的名字,我怎么能得到所有这些标签?
Nokogiri不支持XPath 2.0 matches功能,因此您需要使用Ruby来执行正则表达式:
hits = node_set.xpath("//text()").grep(/\d+:\d+:\d+/).map(&:parent)
p hits.map(&:name)
#=> ["SOME_TAG", "HOLY_TAG", "MAJOR_TAG", "FOO_FOO"]
Run Code Online (Sandbox Code Playgroud)
描述:
该Enumerable#grep方法是简写.select{ |text| regex === text }.
或者,请注意您可以在Nokogiri中定义自己的自定义XPath函数,回调 Ruby,因此您可以假装使用XPath 2.0 matches:
module FindWithRegex
def self.matches(nodes,pattern,flags=nil)
nodes.grep(Regexp.new(pattern,flags))
end
end
hits = node_set.xpath('//*[matches(text(),"\d+:\d+:\d+")]',FindWithRegex)
p hits.map(&:name)
#=> ["SOME_TAG", "HOLY_TAG", "MAJOR_TAG", "FOO_FOO"]
Run Code Online (Sandbox Code Playgroud)
但是,由于每个找到的节点都重新调用它(因此每次从字符串重新创建一个新的正则表达式),因此效率不高:
require 'benchmark'
Benchmark.bm(15) do |x|
N = 10000
x.report('grep and map'){ N.times{
node_set.xpath("//text()").grep(/\d+:\d+:\d+/).map(&:parent)
}}
x.report('custom function'){ N.times{
node_set.xpath('//*[matches(text(),"\d+:\d+:\d+")]',FindWithRegex)
}}
end
#=> user system total real
#=> grep and map 0.437000 0.016000 0.453000 ( 0.442044)
#=> custom function 1.653000 0.031000 1.684000 ( 1.694170)
Run Code Online (Sandbox Code Playgroud)
您可以通过缓存正则表达式加快速度:
module FindWithRegex
REs = {}
def self.matches(nodes,pattern,flags=nil)
nodes.grep(REs[pattern] ||= Regexp.new(pattern,flags))
end
end
#=> user system total real
#=> grep and map 0.437000 0.016000 0.453000 ( 0.442044)
#=> cached regex 0.905000 0.000000 0.905000 ( 0.896090)
Run Code Online (Sandbox Code Playgroud)