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_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中运行。

最新更新