【问题标题】:Approaches for performing a "Grep -f" style query on XML data?对 XML 数据执行“Grep -f”样式查询的方法?
【发布时间】:2013-09-19 14:16:32
【问题描述】:

我在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 文件。

有没有更优雅的方法来做到这一点?还是临时文件真的是我最好的方法?

【问题讨论】:

    标签: xml xpath xmlstarlet


    【解决方案1】:

    您可以编写一个将目标 XML 作为其源文档的 XSLT 样式表,并使用 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>
    

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

    【讨论】:

    • 谢谢拉斯!这种方法效果很好,无需使用 XML Starlet 或临时文件。
    • @OP:很高兴它对你有用!我还应该补充一点,对于非常大的输入文件,您需要一个可以使用流功能的处理器,因此不需要一次将整个输入文件读入内存。例如。我认为带有 XSLT 3.0 的 Saxon 可以做到这一点。同样在这种情况下,您希望反转上述循环,在另一个循环内迭代 $strings-to-match
    【解决方案2】:

    使用我的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 时放弃了所有优化;这已经足够复杂了)。

    【讨论】:

      【解决方案3】:

      不仅如此,尤其是在您反复分析该文件时,请考虑尝试一些 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() 来限制那些包含您正在寻找的针的元素。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-13
        • 2011-02-26
        • 1970-01-01
        • 1970-01-01
        • 2018-02-02
        • 2013-12-15
        相关资源
        最近更新 更多