在XML数据上执行“Grep -f”样式查询的方法?

时间:2013-09-19 14:16:32

标签: xml xpath xmlstarlet

我在this discussion中看到了几个很好的命令行XML操作工具,我正在探索通过脚本而不是编译程序从XML文件中提取数据的新方法。我目前正在尝试xmlstarlet,但我不限于使用此工具。

我有一个包含数万个元素的XML数据文件。我想基于搜索项列表提取这些元素的子集,然后管道或以其他方式将这些元素路由到某些下游脚本和转换。搜索词是简单的字符串 - 不需要正则表达式。如果我在常规文本文件中使用grep执行此操作,我可能会做一些简单的事情:

grep -Ff StringsToSearchFor.txt MassiveFile.txt | [chain of additional commands]

我一直在浏览像xmlstarlet这样的工具的文档,我可以实现这一点,而我能提出的最接近的事情就是这个使用临时文件的丑陋尝试。 (注意,我使用的是Windows):

REM Create tempOutput.xml, with an open root node 

REM %1 is the file containing the list of strings
REM %2 is the target XML file
for /F %%A in (%1) do (
   REM Search for a single matching node, and append the output to tempOutput.xml
   xml sel -I -t -c "path/to/search[targetElement='%%A']" %2 >> tempOutput.xml
)

REM Close root node to tempOutput.xml

REM After this stage, pass tempOutput.xml as the input to downstream XML transforms and tools

毋庸置疑,这真的很难看。

我想有一种可能性就是修改for循环,将-c XPath查询的巨大列表一次性传递给xmlstarlet,但这似乎也不必要地混乱,我认为我仍然会卡住使用tempOutput.xml文件。

有更优雅的方法吗?或者临时文件真的是我最好的方法吗?

3 个答案:

答案 0 :(得分:1)

您可以编写一个XSLT样式表,将目标XML作为源文档,并使用document()读取包含字符串列表的文件。 (如果您正在使用XSLT 2.0,则此文档不必是XML格式。)然后它可以解析字符串列表,并在目标XML文档中查找任何字符串的XPath匹配项:

<xsl:for-each select="$strings-to-match">
  <xsl:for-each select="/path/to/search[targetElement = current()]">
    <!-- whatever format you need to output these in... -->
    <xsl:value-of select="." />
  </xsl:for-each>
</xsl:for-each>

这将输出匹配元素的字符串值(连接的后代文本节点)。您可以根据下游程序的需要输出您想要的任何内容。

答案 1 :(得分:0)

使用我的Xidel,您可以这样写:

xidel --extract-exclude=search-terms  StringsToSearchFor.txt -e '$search-terms := tokenize($raw, $line-ending)[. != ""]' MassiveFile.txt -e 'path/to/search[targetElement = $search-terms]'

但是对于一个大文件来说它可能有点慢(它曾经很快,即使使用流式xml,但是在实现完整的XQuery时我把所有优化都抛弃了;这已经很复杂了)。

答案 2 :(得分:0)

不仅如此,尤其是如果您反复分析该文件,请考虑尝试使用某些XML数据库。他们中的大多数都支持字符串搜索索引,这将大大加快搜索速度。您甚至可能对在XQuery中执行进一步分析感到非常满意。

执行搜索的XPath(XQuery子集)表达式将是

/path/to/search[targetElement = ('list', 'of', 'strings', 'to', 'search', 'for')]

某些实现支持XQuery Full Text,甚至可以增强文本搜索(尤其是使用高效索引):

/path/to/search[targetElement contains text { 'list', 'of', 'strings' }]

阅读这个单词列表很简单,但取决于它的存储方式以及您正在使用的实现。

BaseX是其中一个数据库(和开源软件,免责声明:我有点属于他们)。 galax还支持XQuery Full Text,其他着名的XML数据库和XQuery处理器是eXist DB,Saxon,Sedna和Marklogic。所有这些都有一些命令行工具可以将结果打印到STDOUT,因此您可以将其输入到剩余的处理链中。


如果任何子元素包含该字符串,则所有查询(包括您的查询)都将返回所有父元素。您可能希望使用targetElement/text()来限制包含您正在寻找的针的那些元素。