这可能需要一段时间来解释,但是我有一个文件(xmllist.txt),其中包含多个IDOC XML的路径。xmllist.txt的内容看起来像:
/usr/local/sterlingcommerce/data/archive/sfgprdr/sftpget/2017/dec/week_4/au_dhl_pw_inbound_delivery_from_fom_pfize_pfize /USR/local/sterlingcommerce/data/archive/sfgprdr/sftpget/2017/dec/week_4/au_dhl_pw_inbound_delivery_from_pfizer_pfiers_201712200833310.xml /USR/local/sterlingcommerce/data/archive/sfgprdr/sftpget/2017/dec/week_4/ccmastout_mq_mq_glb_1_20171220154826.xml
我正在尝试创建一个读取每个XML的perl脚本,并将每个XML文件从每个XML文件中的标签docnum,sndprn和rcvprn的值解析到一个管道界定文件中" report.csv"
要注意的另一件事是我的XML文件可能是:全部在一行 - 示例
<?xml version="1.0" encoding="UTF-8"?><ZDELVRY073PL><IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1"><TABNAM>EDI_DC40</TABNAM><MANDT>400</MANDT>
<DOCNUM>0000000443474886</DOCNUM><DOCREL>731</DOCREL><STATUS>30</STATUS>
<DIRECT>1</DIRECT><OUTMOD>4</OUTMOD><IDOCTYP>DELVRY07</IDOCTYP>
<CIMTYP>ZDELVRY073PL</CIMTYP><MESTYP>ZIBDADV</MESTYP><MESCOD>IBG</MESCOD>
<SNDPOR>SAPQ01</SNDPOR><SNDPRT>LS</SNDPRT><SNDPRN>Q01CLNT400</SNDPRN>
<RCVPOR>XMLDIST_MT</RCVPOR><RCVPRT>LS</RCVPRT><RCVPFC>LS</RCVPFC>
<RCVPRN>AU_DHL</RCVPRN>.... </EDI_DC40></IDOC>
或多行XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<INVOIC02>
<IDOC>
<EDI_DC40>
<TABNAM/>
<DOCNUM>0000000658056255</DOCNUM>
<DIRECT/>
<IDOCTYP>INVOIC02</IDOCTYP>
<MESTYP>INVOIC</MESTYP>
<SNDPOR>SAPP01</SNDPOR>
<SNDPRT/>
<SNDPRN>ALE400</SNDPRN>
<RCVPOR>XMLINVOICE</RCVPOR>
<RCVPRT>KU</RCVPRT>
<RCVPRN>C18BASWARE</RCVPRN>
<CREDAT>20171220</CREDAT>
<CRETIM>222323</CRETIM>
</EDI_DC40>
到目前为止,我使用的脚本似乎适用于小型XML。但是,某些XML> 50 MB投掷此错误:
不记忆!内存不足!呼叫称为退出 /usr/opt/perl5/lib/site_perl/5.10.1/xml/sax/base.pm 1941年(#1) (f)通过call_sv()从外部软件包调用的子例程 通过致电出口退出。
不记忆!
所以,这是我正在使用的代码。希望您的帮助对此进行调整:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
# use module
use XML::Simple;
use Data::Dumper;
# create object
my $xml = new XML::Simple;
my $file_list = 'XMLList.txt';
open(my $fh_i, '<:encoding(UTF-8)', $file_list)
or die "Could not open file '$file_list' $!";
my $csv_out = 'report.csv';
open(my $fh_o, '>', $csv_out)
or die "Could not open file '$csv_out' $!";
while (my $row = <$fh_i>) {
$row =~ s/R//g;
my $data = $xml->XMLin($row);
print $fh_o "$data->{IDOC}->{EDI_DC40}->{DOCNUM}|";
print $fh_o "$data->{IDOC}->{EDI_DC40}->{SNDPRN}|";
print $fh_o "$data->{IDOC}->{EDI_DC40}->{RCVPRN}n";
}
close $fh_o;
我建议人们在使用问题时停止使用XML::Simple
。该模块很高兴入门,但这并不意味着是一个长期解决方案。即使那样,请查看为什么XML ::简单"灰心"?
XML::Twig
是我经常用于这些任务的方法。您可以为标签设置处理程序并获取树的那一部分。您正在处理并继续前进。那可能就像这样简单的,我在遇到的时设置了一个子例程来处理每个EDI_DC40
:
use Text::CSV_XS;
use XML::Twig;
my $csv = Text::CSV_XS->new;
my $twig = XML::Twig->new(
twig_handlers => {
'EDI_DC40' => &process_EDI_DC40,
},
);
$twig->parsefile( $ARGV[0] );
sub process_EDI_DC40 {
my( $twig, $thingy ) = @_;
my @values = map { $thingy->first_child( $_ )->text }
qw(DOCNUM RCVPRN SNDPRN);
$csv->say( *STDOUT, @values );
}
首先,如果文件包含newlines,
while (my $row = <$fh_i>){
$row =~ s/R//g;
my $data = $xml->XMLin($row);
将一次从文件中读取一行,并尝试单独在该行上而不是整个文档进行XML转换。我建议您将每个文件拖入一个缓冲区中,然后使用Regex在XMLIN转换之前消除新线和运输返回。另外,如果文件中有任何XML错误,则XMLIN将 DIE 毫不客气地,因此您要在eval block中运行。