在XML中提取多行文本

问题描述:

我有一个XML,如下所示。我想提取<com.eds.travel.fares.ping.response></com.eds.travel.fares.ping.response>之间的文本。 XML以com.eds.travel.fares.ping.response开头并以com.eds.travel.fares.ping.response结尾。在XML中提取多行文本

<?xml version="1.0" encoding="UTF-8"?> 
<!--This is a Ping Response--> 
<com.eds.travel.fares.ping.response xmlns="http://schemas.eds.com/transportation/message/ping/response" targetNamespace="http://schemas.eds.com/transportation/message/ping/response" EchoToken="00c0d1a" TimeStamp="2016-06-21T00:01:48.191" Target="Test" Version="1.07" SequenceNmbr="1466467309030" PrimaryLangID="en" RequestorCompanyCode="1y" RequestorNetworkID="as" SetLocation="zrh"> 
<Headers Trailers="n"> 
    <Result xmlns="http://schemas.eds.com/transportation/message/fares/common" status="success" /> 
</Headers> 
<DataArea> 
    <Pong Message="pong" ServerHostName="usclsefam922.clt.travel.eds.com" ServerPortNumber="8024" ServerMessageCount="1" RegionName="preprod" SystemName="preprods3.1" SystemDate="20160621" SystemTime="148" CodeVersion="$Name: build-2016-06-17-1338 $" /> 
</DataArea> 
<Trailers /> 
</com.eds.travel.fares.ping.response> 

我试着用下面的命令,但没有运气:

cat file.txt | egrep "<com.eds.travel.fares.ping.response>.*</com.eds.travel.fares.ping.response>" 

请指教。

+3

什么是你想准确提取?因为这听起来像是一个解析器的工作......而您正试图解析XML以获得一些语义相同的XML。这真的有点奇怪,听起来像是一个XY问题。 – Sobrique

+1

([无用的'cat'。](https://en.wikipedia.org/wiki/Cat_(Unix)#Useless_use_of_cat)) – Biffen

从我已经试过了,似乎egrep的无法比拟的多条线路,则可以使用pcregrep -M代替

pcregrep -M 'com.eds.travel.fares.ping.response((.|\n)*)com.eds.travel.fares.ping.response' 

的伎俩,我

规则XML之一。不要使用正则表达式。 XML是一种上下文语言,正则表达式不能这样做。你将会有一个黑客,这将是脆弱的,有一天,当XML以完全有效的方式改变时,会神秘地破坏。

所以改为使用解析器。 Perl有几个选项 - 我碰巧喜欢XML::Twig是一个很好的起点(XML::LibXML也很好,但是学习曲线更陡峭)。

而对于这一点,你需要的是:

#!usr/bin/perl 
use strict; 
use warnings; 

use XML::Twig; 

my $twig = XML::Twig -> new (comments => 'drop')->parse (\*DATA); 
$twig -> set_pretty_print('indented_a'); 
$twig -> get_xpath('//com.eds.travel.fares.ping.response',0 ) -> print; 


__DATA__ 
<?xml version="1.0" encoding="UTF-8"?> 
<!--This is a Ping Response--> 
<com.eds.travel.fares.ping.response xmlns="http://schemas.eds.com/transportation/message/ping/response" targetNamespace="http://schemas.eds.com/transportation/message/ping/response" EchoToken="00c0d1a" TimeStamp="2016-06-21T00:01:48.191" Target="Test" Version="1.07" SequenceNmbr="1466467309030" PrimaryLangID="en" RequestorCompanyCode="1y" RequestorNetworkID="as" SetLocation="zrh"> 
<Headers Trailers="n"> 
    <Result xmlns="http://schemas.eds.com/transportation/message/fares/common" status="success" /> 
</Headers> 
<DataArea> 
    <Pong Message="pong" ServerHostName="usclsefam922.clt.travel.eds.com" ServerPortNumber="8024" ServerMessageCount="1" RegionName="preprod" SystemName="preprods3.1" SystemDate="20160621" SystemTime="148" CodeVersion="$Name: build-2016-06-17-1338 $" /> 
</DataArea> 
<Trailers /> 
</com.eds.travel.fares.ping.response> 

此输出 - 的要求:

<com.eds.travel.fares.ping.response 
    EchoToken="00c0d1a" 
    PrimaryLangID="en" 
    RequestorCompanyCode="1y" 
    RequestorNetworkID="as" 
    SequenceNmbr="1466467309030" 
    SetLocation="zrh" 
    Target="Test" 
    TimeStamp="2016-06-21T00:01:48.191" 
    Version="1.07" 
    targetNamespace="http://schemas.eds.com/transportation/message/ping/response" 
    xmlns="http://schemas.eds.com/transportation/message/ping/response"> 
    <Headers Trailers="n"> 
    <Result 
     status="success" 
     xmlns="http://schemas.eds.com/transportation/message/fares/common" 
    /> 
    </Headers> 
    <DataArea> 
    <Pong 
     CodeVersion="$Name: build-2016-06-17-1338 $" 
     Message="pong" 
     RegionName="preprod" 
     ServerHostName="usclsefam922.clt.travel.eds.com" 
     ServerMessageCount="1" 
     ServerPortNumber="8024" 
     SystemDate="20160621" 
     SystemName="preprods3.1" 
     SystemTime="148" 
    /> 
    </DataArea> 
    <Trailers/> 
</com.eds.travel.fares.ping.response> 

这基本上是所有的XML,少标题和评论。技术上你要求什么,但有点微不足道。虽然注意重新格式化 - 重新格式化的XML是完全有效的。这就是为什么基于regex的解决方案突破。

因此,如何来代替:

#!usr/bin/perl 
use strict; 
use warnings; 

use XML::Twig; 

my $twig = XML::Twig -> parsefile ('file.txt'); 

foreach my $pong ($twig -> get_xpath('//Pong')) { 
    foreach my $key(keys %{$pong -> atts}) { 
     print "$key => ", $pong -> att($key),"\n"; 
    } 
} 

与源数据,打印:

CodeVersion => $Name: build-2016-06-17-1338 $ 
RegionName => preprod 
SystemTime => 148 
ServerHostName => usclsefam922.clt.travel.eds.com 
SystemDate => 20160621 
SystemName => preprods3.1 
ServerMessageCount => 1 
ServerPortNumber => 8024 
Message => pong