我有一个简化后的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>'
)
我所知道的只是如何为这个写一个正则表达式:
(d+):(d+):(d+)
我在官方网站上读了一些关于regexp匹配的文章,但是没有答案如何做到这一点。只有将如何调用用户函数的机制转换为xpath方法。
我怎么能得到所有这些标签不知道它的名字通过regexp?
Nokogiri不支持XPath 2.0 matches
函数,因此您需要使用Ruby来执行regex:
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 }
的简写。
或者,请注意,可以在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"]
但是,由于对于每个找到的节点都要重新调用(因此每次都要从字符串中重新创建一个新的regexp),因此它的效率不高:
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)
你可以通过缓存Regex来加快速度:
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)
这是一个纯XPath 1.0解决方案。尽管XPath 1.0中没有原生RegEx功能,但使用标准XPath 1.0函数substring-before()
、substring-after()
和translate()
仍然可以实现:
/*/*[not(translate(substring-before(.,':'),
'0123456789',
''
)
)
and
not(translate
(substring-before(substring-after(.,':'),
':'
),
'0123456789',
''
)
)
and
not(translate
(substring-after(substring-after(.,':'),
':'
),
'0123456789',
''
)
)
]
基于XSLT的验证:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy-of select=
" /*/*[not(translate(substring-before(.,':'),
'0123456789',
''
)
)
and
not(translate
(substring-before(substring-after(.,':'),
':'
),
'0123456789',
''
)
)
and
not(translate
(substring-after(substring-after(.,':'),
':'
),
'0123456789',
''
)
)
]
"/>
</xsl:template>
</xsl:stylesheet>
此XSLT转换仅使用上述表达式进行选择并输出所选节点。当应用于此XML文档(添加了"invalid"元素的提供文档)时:
<PARENT>
<SOME_TAG>12:12:1222</SOME_TAG>
<SOME_TAG2>12a:12:1222</SOME_TAG2>
<HOLY_TAG>12:12:1222</HOLY_TAG>
<HOLY_TAG2>12:12b:1222</HOLY_TAG2>
<MAJOR_TAG>12:12:1222</MAJOR_TAG>
<MAJOR_TAG2>12:12:1222c</MAJOR_TAG2>
<FOO_FOO>12:12:1222</FOO_FOO>
</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>