根据ID列表过滤XML文档

时间:2014-01-30 15:58:30

标签: xml bash

我在TREC format中有一个7GB的XML文档。此文件包含标记DOC,其中有DOCNOTEXT

<FILE>
<DOC>
<DOCNO>abc</DOCNO>
<TEXT>content
of first
doc</TEXT>
</DOC>
<DOC>
<DOCNO>def</DOCNO>
<TEXT>content
of second
doc</TEXT>
</DOC>
<DOC>
<DOCNO>ghi</DOCNO>
<TEXT>content
of third
doc</TEXT>
</DOC>
</FILE>

我想过滤此文档,并且保留DOC文件中包含ids列表的DOCNO:< / p>

abc
ghi

因此输出变为

<FILE>
<DOC>
<DOCNO>abc</DOCNO>
<TEXT>content
of first
doc</TEXT>
</DOC>
<DOC>
<DOCNO>ghi</DOCNO>
<TEXT>content of
third
doc</TEXT>
</DOC>
</FILE>

我的猜测是xml_grep应该有用,但我不能这样做。

2 个答案:

答案 0 :(得分:3)

如果您有xml_grep,我认为模块XML::Twig也已安装。我不知道xml_grep是如何工作的,但你可以通过完整的脚本获得相同的结果,例如:

#!/usr/bin/env perl

use warnings;
use strict;
use XML::Twig;

XML::Twig->new(
    twig_print_outside_roots => 1,
    twig_roots => {
        'DOC' => sub {
            my $docno = $_->next_elt('DOCNO') || next;
            if ( $docno->text_only =~ m/\A(?:abc|ghi)\Z/ ) { 
                $_->print;
            }   
        },  
    },  
    pretty_print => 'indented',
)->parsefile( shift );

搜索每个<DOC>元素,读取下一个元素并提取其文本,使用正则表达式与abcghi进行比较,并仅打印该部分树匹配的情况。

像以下一样运行:

perl script.pl xmlfile

产生(注意空间没有意义,因为它们超出任何元素):

<FILE>

  <DOC>
    <DOCNO>abc</DOCNO>
    <TEXT>content
of first
doc</TEXT>
  </DOC>


  <DOC>
    <DOCNO>ghi</DOCNO>
    <TEXT>content
of third
doc</TEXT>
  </DOC>
</FILE>

答案 1 :(得分:2)

使用awk创建xpath和xmlstarlet来过滤文档:

$ xpath=$(awk '
            BEGIN {printf "//DOC[not("} 
            {printf "%sDOCNO=\"%s\"", sep, $0; sep=" or "}
            END {print ")]"}
        ' ids.txt)

$ echo "$xpath"
//DOC[not(DOCNO="abc" or DOCNO="ghi")]

$ xmlstarlet ed -O -d "$xpath" file.xml
<FILE>
  <DOC>
    <DOCNO>abc</DOCNO>
    <TEXT>content
of first
doc</TEXT>
  </DOC>
  <DOC>
    <DOCNO>ghi</DOCNO>
    <TEXT>content
of third
doc</TEXT>
  </DOC>
</FILE>