XML :: Simple为大型XML返回“Out of memory”错误

时间:2017-12-21 16:33:49

标签: perl xml-parsing xml-simple

这可能需要一段时间来解释,但我有一个文件(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。但是,一些XML&gt; 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;

2 个答案:

答案 0 :(得分:0)

我建议人们在使用XML::Simple时遇到问题时停止使用XML::Twig。该模块很适合入门,但它不是一个长期的解决方案。即便如此,请参阅Why is XML::Simple “Discouraged”?

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 ); } ,因为我遇到它:

KTTTeacherService* teacherService = [KTTTeacherService shared];
// updateCurrentTeacher, for default is nil
KTTeacher *teacher = [[KTTeacher alloc] init];
[KTTTeacherService updateCurrentTeacher:teacher];
KTTeacher* currentTeacher = teacherService.currentTeacher

答案 1 :(得分:-1)

首先,如果文件包含换行符,

  while (my $row = <$fh_i>){
  $row =~ s/\R//g;
  my $data = $xml->XMLin($row);

将从文件中一次读取一行,并尝试单独在该行上进行XML转换,而不是整个文档。我建议您将每个文件放入缓冲区并使用正则表达式在XMLin转换之前消除换行符和回车符。此外,如果文件中存在任何XML错误,XMLin将,因此您希望在eval块中运行它。