Perl:如何将下一个 XML 标签视为前一个标签的子标签?
Perl: How to consider next XML tag as child tag of previous one?
在下面的数据文件中,我想将每个 <Field>
标签视为 <Register>
的子标签,并将每个 <Register>
视为 <Partition>
的子标签。所以,基本上,我试图用相应的 <Register>
和 <Field>
提取每个 <Partition>
细节。由于所有这些标签都是独立的,而不是父子关系,我怎样才能得到我想要的输出?
由于文件很大,我不想把它做成父子关系,因为它需要find/replace和人工干预。
<Partition>
<Name>1</Name>
<Abstract>2</Abstract>
<Description>3</Description>
<ParentName>4</ParentName>
</Partition>
<Partition>
<Name>8</Name>
<Abstract></Abstract>
<Description>9</Description>
<ParentName>10</ParentName>
</Partition>
<Register>
<Name>12</Name>
<Abstract></Abstract>
<Description>13</Description>
<ParentName>14</ParentName>
<Size>32</Size>
<AccessMode>15</AccessMode>
<Type>16</Type>
</Register>
<Field>
<Name>17</Name>
<Abstract></Abstract>
<Description></Description>
<ParentName></ParentName>
</Field>
<Field>
.
.
.
</Field>
<Register>
.
.
.
</Register>
<Field>
.
.
.
</Field>
<Field>
.
.
.
</Field>
<Partition>
<Name>88</Name>
<Abstract></Abstract>
<Description></Description>
<ParentName>55</ParentName>
</Partition>
<Register>
.
.
.
</Register>
<Field>
.
.
.
</Field>
<Partition>
.
.
.
</Partition>
<Partition>
.
.
.
</Partition>
<Partition>
.
.
.
</Partition>
<Register>
.
.
.
</Register>
我正在使用 XML::Twig
包,这是我的代码片段:
foreach my $register ( $twig->get_xpath('//Register') ) # get each <Register>
{
#print $register, "\n";
my $reg_name = $register->first_child('Name')->text;
my $reg_abstract= $register->first_child('Abstract')->text;
my $reg_description= $register->first_child('Description')->text;
.
.
.
foreach my $xml_field ($register->get_xpath('Field'))
{
my $reg_field_name= $xml_field->first_child('Name')->text;
my $reg_field_abstract= $xml_field->first_child('Abstract')->text;
#print "$reg_field_name \n";
.
.
.
}
}
根据你的评论,如果你想用 Register
和 Field
元素作为 Partition
元素的子元素来重写文件,你可以这样做:
最简单的解决方案,将整个文件加载到内存中:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $test_file= 'test.xml';
XML::Twig->new( twig_handlers => { 'Register|Field' => \&child,
},
pretty_print => 'indented',
)
->parsefile( $test_file)
->print;
sub child
{ my( $t, $child)= @_;
$child->move( last_child => $child->prev_sibling( 'Partition'));
}
由于您提到文件可能非常大,下面是一个稍微复杂的版本,它只在内存中保留 2 Partition
个元素(包括第一个的新子元素)。解析 Partition
时,它使用 flush_up_to
刷新树,直到之前的 Partition
:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $test_file= 'test.xml';
XML::Twig->new( twig_handlers => { 'Partition' => \&parent,
'Register|Field' => \&child,
},
pretty_print => 'indented',
)
->parsefile( $test_file);
sub child
{ my( $t, $child)= @_;
$child->move( last_child => $child->prev_sibling( 'Partition'));
}
sub parent
{ my( $t, $partition)= @_;
if( my $prev_partition = $partition->prev_sibling( 'Partition'))
{ $t->flush_up_to( $prev_partition); }
}
请注意,由于使用了 flush_up_to
,在解析结束时树的其余部分会自动刷新
如果您需要将 XML 写入特定文件,而不是 STDOUT,您还可以将文件句柄传递给 flush_up_to
。
顺便说一句,我已经编写了非常基本的代码来将 Field
作为 child 转换为 Register
,将 Register
作为 child 转换为 Partition
:
use strict;
#use warnings;
use XML::Twig;
use Data::Dumper;
use Data::Alias;
my $input_xml_file = "gpon.xml";
open (IN_FILE,$input_xml_file);
my @input_file = <IN_FILE>;
for (my $line=0;$line<@input_file;$line++)
{
if ($input_file[$line] =~ /<\/Partition>/ && $input_file[$line+1] =~ /<Register>/)
{
$input_file[$line] = '';
}
if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Partition>/)
{
$input_file[$line] = "</Field>
</Register>
</Partition>
";
}
if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Register>/)
{
$input_file[$line] = "</Field>
</Register>
";
}
if ($input_file[$line] =~ /<\/Register>/ && $input_file[$line+1] =~ /<Field>/ )
{
$input_file[$line] = '';
}
}
#print OUT_FILE "</Register>";
close(IN_FILE);
open (OUT_FILE,'>gpon_modified.xml');
foreach (@input_file)
{
print OUT_FILE "$_";
}
print OUT_FILE "</Register>
</Partition>";
close (OUT_FILE);
在下面的数据文件中,我想将每个 <Field>
标签视为 <Register>
的子标签,并将每个 <Register>
视为 <Partition>
的子标签。所以,基本上,我试图用相应的 <Register>
和 <Field>
提取每个 <Partition>
细节。由于所有这些标签都是独立的,而不是父子关系,我怎样才能得到我想要的输出?
由于文件很大,我不想把它做成父子关系,因为它需要find/replace和人工干预。
<Partition>
<Name>1</Name>
<Abstract>2</Abstract>
<Description>3</Description>
<ParentName>4</ParentName>
</Partition>
<Partition>
<Name>8</Name>
<Abstract></Abstract>
<Description>9</Description>
<ParentName>10</ParentName>
</Partition>
<Register>
<Name>12</Name>
<Abstract></Abstract>
<Description>13</Description>
<ParentName>14</ParentName>
<Size>32</Size>
<AccessMode>15</AccessMode>
<Type>16</Type>
</Register>
<Field>
<Name>17</Name>
<Abstract></Abstract>
<Description></Description>
<ParentName></ParentName>
</Field>
<Field>
.
.
.
</Field>
<Register>
.
.
.
</Register>
<Field>
.
.
.
</Field>
<Field>
.
.
.
</Field>
<Partition>
<Name>88</Name>
<Abstract></Abstract>
<Description></Description>
<ParentName>55</ParentName>
</Partition>
<Register>
.
.
.
</Register>
<Field>
.
.
.
</Field>
<Partition>
.
.
.
</Partition>
<Partition>
.
.
.
</Partition>
<Partition>
.
.
.
</Partition>
<Register>
.
.
.
</Register>
我正在使用 XML::Twig
包,这是我的代码片段:
foreach my $register ( $twig->get_xpath('//Register') ) # get each <Register>
{
#print $register, "\n";
my $reg_name = $register->first_child('Name')->text;
my $reg_abstract= $register->first_child('Abstract')->text;
my $reg_description= $register->first_child('Description')->text;
.
.
.
foreach my $xml_field ($register->get_xpath('Field'))
{
my $reg_field_name= $xml_field->first_child('Name')->text;
my $reg_field_abstract= $xml_field->first_child('Abstract')->text;
#print "$reg_field_name \n";
.
.
.
}
}
根据你的评论,如果你想用 Register
和 Field
元素作为 Partition
元素的子元素来重写文件,你可以这样做:
最简单的解决方案,将整个文件加载到内存中:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $test_file= 'test.xml';
XML::Twig->new( twig_handlers => { 'Register|Field' => \&child,
},
pretty_print => 'indented',
)
->parsefile( $test_file)
->print;
sub child
{ my( $t, $child)= @_;
$child->move( last_child => $child->prev_sibling( 'Partition'));
}
由于您提到文件可能非常大,下面是一个稍微复杂的版本,它只在内存中保留 2 Partition
个元素(包括第一个的新子元素)。解析 Partition
时,它使用 flush_up_to
刷新树,直到之前的 Partition
:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $test_file= 'test.xml';
XML::Twig->new( twig_handlers => { 'Partition' => \&parent,
'Register|Field' => \&child,
},
pretty_print => 'indented',
)
->parsefile( $test_file);
sub child
{ my( $t, $child)= @_;
$child->move( last_child => $child->prev_sibling( 'Partition'));
}
sub parent
{ my( $t, $partition)= @_;
if( my $prev_partition = $partition->prev_sibling( 'Partition'))
{ $t->flush_up_to( $prev_partition); }
}
请注意,由于使用了 flush_up_to
,在解析结束时树的其余部分会自动刷新
如果您需要将 XML 写入特定文件,而不是 STDOUT,您还可以将文件句柄传递给 flush_up_to
。
顺便说一句,我已经编写了非常基本的代码来将 Field
作为 child 转换为 Register
,将 Register
作为 child 转换为 Partition
:
use strict;
#use warnings;
use XML::Twig;
use Data::Dumper;
use Data::Alias;
my $input_xml_file = "gpon.xml";
open (IN_FILE,$input_xml_file);
my @input_file = <IN_FILE>;
for (my $line=0;$line<@input_file;$line++)
{
if ($input_file[$line] =~ /<\/Partition>/ && $input_file[$line+1] =~ /<Register>/)
{
$input_file[$line] = '';
}
if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Partition>/)
{
$input_file[$line] = "</Field>
</Register>
</Partition>
";
}
if ($input_file[$line] =~ /<\/Field>/ && $input_file[$line+1] =~ /<Register>/)
{
$input_file[$line] = "</Field>
</Register>
";
}
if ($input_file[$line] =~ /<\/Register>/ && $input_file[$line+1] =~ /<Field>/ )
{
$input_file[$line] = '';
}
}
#print OUT_FILE "</Register>";
close(IN_FILE);
open (OUT_FILE,'>gpon_modified.xml');
foreach (@input_file)
{
print OUT_FILE "$_";
}
print OUT_FILE "</Register>
</Partition>";
close (OUT_FILE);