通过CSV迭代并创建XML文件



我试图在perl中解析CSV文件,并将某些列的信息粘贴到XML文件中。我从来没有在perl中做任何事情,我的想法是将数据存储到数组中,然后在构建它时将信息从数组中抽出。

我确定我做错了几件事,因为我没有得到我期望的价值,而是看起来像内存中的数组地址(以下是一个示例: ARRAY(0x35e9360)

有人可以帮助我,并将我指向更好的解决方案吗?

这是所讨论的代码:

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>

此程序似乎按照您需要

链接:

  • Text::CSV
  • XML::Twig

在逆转您的代码以发现您的目标之后,我发现这确实是一个相当简单的问题。如果您在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>



阅读了您的评论后(应该将类似的内容编辑为问题)说"我正在从头开始构建[XML数据]。有一个sub buildxml" 我认为这更有可能成为您需要的。使用XML::Twig,最简单地解析了一些XML文本,而不是创建和链接单个XML::Twig::Elt对象

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

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>

getline Text::CSV的方法返回arrayref

它使用$ io-> getline()从io对象$ io读取一行,然后将此行解析为阵列参考。

ARRAY(0x35e9360)确实是您打印出数组参考时所获得的。这是通常的,许多解析器通常会返回对行的阵列的引用。因此,您需要取消通常,通常是由@{$arrayref}来的,但是在这种情况下,没有歧义,可以丢下卷发, @$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 "@colsn";
    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();
}

此打印(当@cols及其打印是未接受的时)的CSV文件

ID名称POS1 A 2652 B 950...

所以我们读了文件。

除了使用CSV值的部分外,XML处理是从问题中复制的。我们采用当前行的第一个元素,即$rowref->[0],因为$rowref是参考。(或使用从重新推荐的数组中使用的元素,$cols[0]。)

我不知道预期的输出,但它是由模板构建的,对于此代码似乎还可以。


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

最新更新