XML :: Simple为大型XML返回“Out of memory”错误
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XML :: Simple为大型XML返回“Out of memory”错误相关的知识,希望对你有一定的参考价值。
这可能需要一段时间来解释,但我有一个文件(XMLList.txt),其中包含多个IDOC XML的路径。 XMLList.txt的内容如下所示:
/usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/AU_DHL_PW_Inbound_Delivery_from_Pfizer_20171220071754.xml /usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/AU_DHL_PW_Inbound_Delivery_from_Pfizer_20171220083310.xml /usr/local/sterlingcommerce/data/archive/SFGprdr/SFTPGET/2017/Dec/week_4/CCMastOut_MQ_GLB_1_20171220154826.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。但是,一些> 50 MB的XML会抛出此错误:
内存不足!内存不足!回调在/usr/opt/perl5/lib/site_perl/5.10.1/XML/SAX/Base.pm第1941行调用退出(#1)(F)通过call_sv()从外部包调用的子程序通过调用exit退出。
内存不足!
所以,这是我正在使用的代码。希望你的帮助调整一下:
#!/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}
";
}
close $fh_o;
我建议人们在使用时遇到问题时停止使用XML::Simple
。该模块很适合入门,但它不是一个长期的解决方案。即便如此,请参阅Why is XML::Simple “Discouraged”?
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 );
}
首先,如果文件包含换行符,
while (my $row = <$fh_i>){
$row =~ s/R//g;
my $data = $xml->XMLin($row);
将从文件中一次读取一行,并尝试单独在该行上进行XML转换,而不是整个文档。我建议您将每个文件放入缓冲区并使用正则表达式在XMLin转换之前消除换行符和回车符。此外,如果文件中存在任何XML错误,XMLin将毫不客气地死亡,因此您希望在eval块中运行它。
以上是关于XML :: Simple为大型XML返回“Out of memory”错误的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 PHP 将 Simple XML 解释为 HTML?