我试图在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];
。