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"]
Описано:
- Поиск всех текстовых узлов по всему документу.
- Сокращение списка до тех, которые соответствуют требуемому регулярному выражению.
- Сопоставление списка с родительскими элементами каждого текстового узла.
Метод Enumerable#grep
является сокращением для .select{ |text| regex === text }
.
В качестве альтернативы , обратите внимание, что вы может определять ваши собственные пользовательские функции XPath в Nokogiri , которые обращаются к 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"]
Однако из-зак тому, что это повторно вызывается для каждого найденного узла (и, таким образом, каждый раз заново создается новое регулярное выражение из строки), это не так эффективно:
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)
Вы можете ускорить его, кэшируяРегулярное выражение:
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)