迭代CSV并创建XML文件

时间:2016-06-28 07:15:42

标签: arrays xml perl csv

我正在尝试在Perl中解析CSV文件,并将某些列的信息粘贴到XML文件中。我从未在Perl中做过任何事情,我的想法是将数据存储到数组中,然后在构建数据时将信息从数组中拉出来。

我确定我做错了几件事,因为我没有得到我期望的值,而是看起来像内存中的数组地址(这是一个例子:ARRAY(0x35e9360)。< / p>

有人可以帮助我并指出一个更好的解决方案吗?

以下是相关代码:

use Text::CSV;
use utf8;
use XML::Simple qw(XMLout);
use XML::Twig;
use File::Slurp;
use Encode;

&buildXML();

my $csv = Text::CSV->new( { binary => 1 } )    # should set binary attribute.
        or die "Cannot use CSV: " . Text::CSV->error_diag();

$csv = Text::CSV->new( { sep_char => '|' } );
$csv = Text::CSV_XS->new( { allow_loose_quotes => 1 } );

my $t = XML::Twig->new( pretty_print => indented );
$t->parsefile('output.xml');

$out_file = "output.xml";
open( my $fh_out, '>>', $out_file ) or die "unable to open $out_file for writing: $!";

my $root = $t->root;                           #get the root

open my $fh, "<:encoding(utf8)", "b.txt" or die "text.txt: $!";

while ( my $row = $csv->getline($fh) ) {

    my @rows = $row;

    $builds = $root->first_child();            # get the builds node
    $xcr    = $builds->first_child();          #get the xcr node

    my $xcrCopy = $xcr->copy();                #copy the xcr node
    $xcrCopy->paste( after, $xcr );            #paste the xcr node

    $xcr->set_att( id => "@rows[0]" );
    print {$fh_out} $t->sprint();
}

$csv->eof or $csv->error_diag();

这是一个测试文件:

ID|Name|Pos
1|a|265
2|b|950
3|c|23
4|d|798
5|e|826
6|f|935
7|g|852
8|h|236
9|i|642

这是由buildXML() sub构建的XML。

<?xml version='1.0' standalone='yes'?>
<project>
  <builds>
    <xcr id="" name="" pos="" />          
  </builds>
</project>

2 个答案:

答案 0 :(得分:3)

此程序似乎按您的要求执行

链接:

在对代码进行逆向工程以发现您的目标之后,我发现它确实是一个相当简单的问题。如果你在CSV文件中为每一行添加一个新的xcr元素来解释你的意图,并且属性对应于列

,那将会有很大的帮助。

您可能根本不需要XML模板文件,或者只是具有空属性的模板xcr元素是多余的?我也想知道你是否想跳过CSV文件中的标题行?这些更改很简单,但我已将代码保留在最简单的状态

use utf8;
use strict;
use warnings 'all';
use autodie;

use Text::CSV;
use XML::Twig;
use Encode;

use constant XML_FILE => 'output.xml';
use constant CSV_FILE => 'b.txt';

build_xml(XML_FILE);

my $csv = Text::CSV->new( {
    sep_char           => '|',
    binary             => 1,
    allow_loose_quotes => 1,   # This is brought forward. Probably unnecessary
} );

my $t = XML::Twig->new(
    pretty_print => 'indented',
);

$t->parsefile(XML_FILE);
my ($xcr) = $t->findnodes('/project/builds/xcr');

open my $fh, '<:encoding(utf8)', CSV_FILE;

while ( my $row = $csv->getline($fh) ) {

    my ($id, $name, $pos) = @$row;

    my $xcr_copy = $xcr->copy;
    $xcr_copy->set_att( id => $id, name => $name, pos => $pos );
    $xcr_copy->paste( last_child => $xcr->parent );
}

$t->print;


sub build_xml {

    open my $fh, '>', shift;

    print $fh <<__END_XML__;
<?xml version='1.0' standalone='yes'?>
<project>
  <builds>
    <xcr id="" name="" pos="" />          
  </builds>
</project>
__END_XML__

}

输出

<?xml version="1.0" standalone="yes"?>
<project>
  <builds>
    <xcr id="" name="" pos=""/>
    <xcr id="ID" name="Name" pos="Pos"/>
    <xcr id="1" name="a" pos="265"/>
    <xcr id="2" name="b" pos="950"/>
    <xcr id="3" name="c" pos="23"/>
    <xcr id="4" name="d" pos="798"/>
    <xcr id="5" name="e" pos="826"/>
    <xcr id="6" name="f" pos="935"/>
    <xcr id="7" name="g" pos="852"/>
    <xcr id="8" name="h" pos="236"/>
    <xcr id="9" name="i" pos="642"/>
  </builds>
</project>


阅读完评论后(这样的内容应编辑成问题)说&#34;我正在从头开始构建[XML数据]。有一个sub buildXML&#34; 我认为这更有可能是你需要的。使用XML::Twig,最简单的方法是解析一些XML文本,而不是创建和链接单个XML::Twig::Elt对象

$t对象首先没有xcr个对象。它们都是通过XML::Twig::Elt->new创建的,并粘贴为last_child元素的builds

require v5.14.1;  # For autodie

use utf8;
use strict;
use warnings 'all';
use autodie;

use Text::CSV;
use XML::Twig;
use Encode;

use constant XML_FILE => 'output.xml';
use constant CSV_FILE => 'b.txt';

my $t = XML::Twig->new(
    pretty_print => 'indented',
);

$t->parse(<<END_XML);
<project>
  <builds/>
</project>
END_XML

my ($builds) = $t->findnodes('/project/builds');


my $csv = Text::CSV->new( {
    sep_char => '|',
    binary => 1,
    allow_loose_quotes => 1,
} );

{
    open my $fh, '<:encoding(utf8)', CSV_FILE;
    <$fh>; # Drop the header line

    while ( my $row = $csv->getline($fh) ) {

        my ($id, $name, $pos) = @$row;

        my $xcr = XML::Twig::Elt->new(xcr => {
            id   => $id,
            name => $name,
            pos  => $pos
        });

        $xcr->paste( last_child => $builds );
    }
}

open my $fh, '>encoding(utf-8)', XML_FILE;
$t->set_output_encoding('UTF-8');
$t->print($fh, 'indented');

输出

<?xml version="1.0" encoding="UTF-8"?><project>
  <builds>
    <xcr id="1" name="a" pos="265"/>
    <xcr id="2" name="b" pos="950"/>
    <xcr id="3" name="c" pos="23"/>
    <xcr id="4" name="d" pos="798"/>
    <xcr id="5" name="e" pos="826"/>
    <xcr id="6" name="f" pos="935"/>
    <xcr id="7" name="g" pos="852"/>
    <xcr id="8" name="h" pos="236"/>
    <xcr id="9" name="i" pos="642"/>
  </builds>
</project>

答案 1 :(得分:1)

Text::CSV的{​​{3}}方法返回一个arrayref

  

它使用$ io-&gt; getline()从IO对象$ io中读取一行,并将此行解析为数组引用。

ARRAY(0x35e9360)确实是打印出数组引用时得到的。通常,许多解析器通常返回对行的数组的引用。因此,您需要取消引用,通常是@{$arrayref},但在这种情况下,没有歧义,可以删除curles,@$arrayref

use warnings;
use strict;
use Text::CSV_XS;
use XML::Twig;

my $csv = Text::CSV_XS->new (
    { binary => 1, sep_char => '|',  allow_loose_quotes => 1 }
) or die "Cannot use CSV: " . Text::CSV->error_diag();

my $t = XML::Twig->new(pretty_print => 'indented');
$t->parsefile('output.xml');
my $out_file = 'output.xml';
open my $fh_out, '>>', $out_file  or die "Can't open $out_file for append: $!";
my $root = $t->root;

my $file = 'b.txt';
open my $fh, "<:encoding(UTF-8)", $file  or die "Can't open $file: $!";

while (my $rowref = $csv->getline($fh)) {
    #my @cols = @$rowref;
    #print "@cols\n";

    my $builds = $root->first_child();  # get the builds node
    my $xcr = $builds->first_child();   # get the xcr node
    my $xcrCopy = $xcr->copy();         # copy the xcr node
    $xcrCopy->paste('after', $xcr);     # paste the xcr node
    $xcr->set_att(id => $rowref->[0]);  # or $cols[0];

    print $fh_out $t->sprint();
}

为CSV文件打印(当@cols及其打印未被注释时)

ID Name Pos
1 a 265
2 b 950
...

所以我们已经读好了文件。

从问题中复制XML处理,但使用CSV值的部分除外。我们取当前行的第一个元素,$rowref->[0],因为$rowref是一个引用。 (或者使用取消引用数组中的元素$cols[0]。)

我不知道预期的输出是什么,但它是从模板构建的,对于此代码似乎没问题。

请注意。数组的单个元素是标量,因此它带有$ - 所以, $cols[0]。如果要提取多个列,可以使用数组切片,在这种情况下,结果是一个数组,因此它需要@,例如@cols[0,2]是一个数组,第一和第三个要素。然后可以将其分配给列表,例如my ($c1, $c3) = @cols[0,2];