【问题标题】:Sorting & Merging XML Documents with Perl / XML::Twig使用 Perl / XML::Twig 对 XML 文档进行排序和合并
【发布时间】:2011-12-21 13:34:39
【问题描述】:

我在一个目录中有许多 XML 文件需要排序并合并到一个文件中。文件格式如下:

文件1.xml:

<?xml version="1.0" encoding="utf-8"?>
<doctypea>
  <header someattr="1">
    <docnumber>111</docnumber>
  </header>
</doctypea>

文件2.xml:

<?xml version="1.0" encoding="utf-8"?>
<doctypea>
  <header someattr="1">
    <docnumber>112</docnumber>
  </header>
</doctypea>

文件3.xml:

<?xml version="1.0" encoding="utf-8"?>
<doctypeb>
  <header someattr="1">
    <docnumber>111</docnumber>
  </header>
</doctypeb>

File4.xml:

<?xml version="1.0" encoding="utf-8"?>
<doctypeb>
  <header someattr="1">
    <docnumber>112</docnumber>
  </header>
</doctypeb>

此目录中的所有文件都需要按以下标准排序:

  1. 文件编号
  2. 文档类型(a 或 b)

然后他们需要合并,所以输出文件应该是这样的:

<?xml version="1.0" encoding="utf-8"?>
<doctypea>
  <header someattr="1">
    <docnumber>111</docnumber>
  </header>
</doctypea>
<doctypeb>
  <header someattr="1">
    <docnumber>111</docnumber>
  </header>
</doctypeb>
<doctypea>
  <header someattr="1">
    <docnumber>112</docnumber>
  </header>
</doctypea>
<doctypeb>
  <header someattr="1">
    <docnumber>112</docnumber>
  </header>
</doctypeb>

为了实现这一点,我尝试在 Perl 中使用 XML:Twig。到目前为止,我有以下代码:

use XML::Twig;

my $xmldir = "/xmlfiles";
my $parser = XML::Twig->new(pretty_print => 'indented');

opendir(DIR, "$xmldir");
my @FILES= readdir(DIR);
closedir(DIR);

foreach (@FILES) {
        if ($_ ne "." && $_ ne "..") {
                print "reading file: $xmldir/$_\n";
                $parser->parsefile("$xmldir/$_");
        }
}

此时我似乎无法找出正确的语法来从解析器中获取我想要的元素。

1.如何获取根元素的值(“doctypea”或“doctypeb”)?

2。我假设我需要 (1) 才能将节点解析到 docnumber 字段?

然后我的计划是构建某种带有 doctype%number 的 has 以进行排序,我不确定将它们与它合并的最简单方法。

感谢任何建议!

【问题讨论】:

  • 这些不是 XML 文件。它们被 XML-Twig 和任何其他 XML 处理器拒绝,这是理所当然的。属性值必须加引号,并且每个根元素的开始和结束标记的名称必须匹配。
  • 抱歉,我手动模拟了一个更简单的示例,使它们无效。它们是有效的 XML 文件。我更正了这个例子。

标签: xml perl xml-twig


【解决方案1】:

请在下面找到可以帮助您入门的小示例。它展示了如何从类似于您的 XML 文件中获取数据(我修复了标签以匹配并引用 someattr 值以获取有效的 XML)。您可以使用类似的方法来收集您需要的数据并生成输出。

use XML::Twig;

XML::Twig->new(twig_handlers => {
    '/*'        => sub { print $_->gi;           },     # doctypea
    'docnumber' => sub { print $_->trimmed_text; },     # 111
})->parse(\*DATA);    # use parsefile('xxx.xml') to parse a file

__DATA__
<?xml version="1.0" encoding="utf-8"?>
<doctypea>
  <header someattr="1">
    <docnumber>111</docnumber>
  </header>
</doctypea>

【讨论】:

  • 当我尝试这个时,我得到:unrecognized expression in handler: '/*'
  • @toolic:我正在使用 perl v5.8.5 (linux) 和 XML:Twig v 3.13。我没有能力升级这些。
  • @ChuckMac - XML::Twig 是纯 perl 模块,因此您可以下载最新版本并将文件 Twig.pmTwig/XPath.pm 放入您的 PERL5LIB 目录 XML 中。这样您就可以拥有最新版本而无需更新任何内容。根据cpantesters,即使在 perl 5.8.5 上也应该可以正常工作。
  • @bvr:我能够将最新版本的 XML::Twig 放在单独的目录中并直接要求它,现在可以使用。谢谢!
【解决方案2】:

正如 daxim 所注意到的,您的文件不是有效的 XML,但您可以使用正则表达式处理它们。如果文件不是太大,您可以将文件转换成单独的字符串,然后根据它们的内容进行排序。

use File::Slurp qw( read_dir ) ;
my $xmldir=".";
my %files = map {
        s/^.*$//m; 
        /<doctype([ab])>/; my $x=ord($1) - ord('a');
        /<docnumber>(\d+)</docnumber>/; $x += 10*$2;
        $x => $_
    } read_dir($xmldir);
print join("", map { $files{$_} } sort keys %files);

我还没有调试过这段代码。 print join("", values %files); 也可能有效。

【讨论】:

  • 抱歉,我手动模拟了一个更简单的示例,使它们无效。它们是有效的 XML 文件。我纠正了这个例子。它们比这大得多,我只是简化了目标。
猜你喜欢
  • 1970-01-01
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多