如何使用 grep 基于属性用 perl 解析 xml 文件

How to parse xml file with perl based on attribute using grep

我是 perl 的新手并且一直在努力。我有一个具有以下结构的 xml 文件,但有数千个条目:

test.xml

<msms_pipeline_analysis>
    <spectrum_query spectrum="H_TPP08.04885.04885.2" start_scan="4885" end_scan="48887">
        <search_result>
          <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
          </search_hit>
        </search_result>
    </spectrum_query>
    <spectrum_query spectrum="L_TPP08.05765.04785.2" start_scan="4885" end_scan="48856">
        <search_result>
          <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
          </search_hit>
        </search_result>        
    </spectrum_query>
    <spectrum_query spectrum="L_TPP10.87945.3485.2" start_scan="4885" end_scan="4885">
        <search_result>
          <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
          </search_hit>
        </search_result>        
    </spectrum_query>
</msms_pipeline_analysis>

我需要 parse/delete "spectrum_query" 节点 包含在属性 "spectrum" 本例中的字符串 "TPP08" 即实际上是第一个下划线和第一个点之间的内容(稍后我想对 TPP09、TPP10 等进行子集化) ,例如。

H_TPP08.04885.04885.2

并保留文件及其结构。

通过搜索,我想出了很多解决方案,这些解决方案着眼于删除满足某个属性的节点。在我的例子中,这样的解决方案可以删除一个有问题的节点:

#!/urs/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig -> new ('pretty_print' => 'indented' ) -> parsefile ( 'test.xml' ); 
foreach my $element ( $twig -> get_xpath('spectrum_query[@spectrum="H_TPP08.04885.04885.2"]') ) {
   $element -> delete;
}

$twig -> print; 

open XML, ">output.xml";
print XML $twig->toString();
close XML;

删除第一个节点。但只有特定的一个,而真正的文件有数千个条目。此外,我想 保留 满足标准的那些,反之,我将不得不 运行 不包含频谱 TPP08 的所有其他条目的脚本(例如 TPP09、TPP10 等)。

关于字符串的判断,目前我是这样来的

$string = qw(H_TPP08.05164.05164.2);
my ($substring2) = $string =~ m:.*_(.+?)?\.:;
print "$substring2\n";

输出 TPP08 我想要的,因为我需要保持节点 H_TPP08.XXXX 或 L_TPP08.XXXX

到目前为止,我还没有找到是否有办法像 R 中那样用“!”做一个负子集。 grep,并将 grep 包含在属性字符串的匹配中,以便可以对其进行解析。对于我最有可能阅读的内容,我需要用所有条目的属性字符串创建一个数组

my @array = map { $tag -> att('spectrum') } $twig -> get_xpath('//spectrum_query');

然后依次评估 grep 之后的每个条目并将其与匹配的字符串进行比较,然后只保留满足该条件的节点。但是我无法用我的基本 perl 知识来解决这个问题。

任何帮助将不胜感激!谢谢

use strict;
use warnings;

use XML::Twig;

my $xml = <<'EOF';
<msms_pipeline_analysis>
  <spectrum_query spectrum="H_TPP08.04885.04885.2" start_scan="4885" end_scan="48887">
    <search_result>
      <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
      </search_hit>
    </search_result>
  </spectrum_query>
  <spectrum_query spectrum="L_TPP08.05765.04785.2" start_scan="4885" end_scan="48856">
    <search_result>
      <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
      </search_hit>
    </search_result>
  </spectrum_query>
  <spectrum_query spectrum="L_TPP10.87945.3485.2" start_scan="4885" end_scan="4885">
    <search_result>
      <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1">
      </search_hit>
    </search_result>
  </spectrum_query>
</msms_pipeline_analysis>
EOF

my $twig = XML::Twig->new(pretty_print => 'indented')->parse($xml);

for my $element ($twig->get_xpath('/msms_pipeline_analysis/spectrum_query')) {
    next if $element->att('spectrum') =~ /TPP08/;
    $element->delete;
}

$twig->print;

输出:

<msms_pipeline_analysis>
  <spectrum_query end_scan="48887" spectrum="H_TPP08.04885.04885.2" start_scan="4885">
    <search_result>
      <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1"></search_hit>
    </search_result>
  </spectrum_query>
  <spectrum_query end_scan="48856" spectrum="L_TPP08.05765.04785.2" start_scan="4885">
    <search_result>
      <search_hit calc_neutral_pep_mass="2348.060995306391" hit_rank="1"></search_hit>
    </search_result>
  </spectrum_query>
</msms_pipeline_analysis>

您可以在属性上使用 get_xpath 和正则表达式

foreach my $element ( $twig -> get_xpath('spectrum_query[@spectrum =~ /^(?!\w_TPP08\.)/]') ) {
   $element -> delete;
}

或者您可以检查每个节点的属性匹配:

foreach my $element ( $twig -> get_xpath('spectrum_query[@spectrum]') ) {
  if ($element->att('spectrum')!~ m/^\w_TPP08\./) {
    $element -> delete;
  }
}

最 "twiggish" 的方法是遍历文件并丢弃不需要的元素,同时输出其余元素。

  • 使用 twig_roots 匹配正确的 spectrum_query 元素,不对它们做任何事情,有效地丢弃它们,
  • 让 XML 的其余部分按原样输出,使用 twig_print_outside_roots

这将非常节省内存,因为几乎没有任何内容会保留在内存中。

#!/usr/bin/env perl

use strict;
use warnings;

use autodie qw(open);

use XML::Twig;

my $target = 'TPP08';
my $input  = 'test.xml';
my $output = 'output.xml';
open( my $out, '>:utf8', $output);

XML::Twig->new( twig_roots          => { qq{spectrum_query[\@spectrum=~/^[^_]*_$target\./]} => 1, },
                twig_print_outside_roots => $out,
              )
         ->parsefile( $input);

请注意,每个丢弃的元素都会在输出中产生一个空行,白色 space 管理很棘手。如果重要,您可以使用 grep -v 或使用 xml_pp.

摆脱那些