看起来你正在使用XML。你想解析它吗?
嘿,我以前从未用过Perl解析,但有入门教程等资料… 这并不是非常直接明了。通过阅读XML::SAX::ParserFactory和XML::SAX::Base,我编写出了你在本答案底部看到的代码。
问题已更新,不再有相邻的行;之前是:
好的,我看到你在整个文件中有两个日期匹配的<start>
标签和两个日期匹配的<end>
标签,但它们位于不同的部分。如果所有重复的行也是有效相邻的,就像你的示例中一样,你只需要使用GNU Coreutils的uniq
命令或等效命令。该命令可以通过正确使用LC_COLLATE
环境变量设置来忽略大小写,但老实说,我很难找到一个例子或者阅读如何使用LC_COLLATE
来忽略大小写。
继续使用解析器:
use XML::SAX;
my $parser = XML::SAX::ParserFactory->parser(
Handler => TestXMLDeduplication->new()
);
my $ret_ref = $parser->parse_file(\*TestXMLDeduplication::DATA);
close(TestXMLDeduplication::DATA);
print "\n\nDuplicates skipped: ", $ret_ref->{skipped}, "\n";
print "Duplicates cut: ", $ret_ref->{cut}, "\n";
package TestXMLDeduplication;
use base qw(XML::SAX::Base);
my $inUserinterface;
my $inUpath;
my $upathSeen;
my $defaultOut;
my $currentOut;
my $buffer;
my %seen;
my %ret;
sub new {
my $type = shift;
$defaultOut = *STDOUT;
$currentOut = $defaultOut;
return bless {}, $type;
}
sub start_document {
%ret = ();
$inUserinterface = 0;
$inUpath = 0;
$upathSeen = 0;
}
sub end_document {
return \%ret;
}
sub start_element {
my ($self, $element) = @_;
if ('userinterface' eq $element->{Name}) {
$inUserinterface++;
%seen = ();
}
if ('upath' eq $element->{Name}) {
$buffer = q{};
undef $currentOut;
open($currentOut, '>>', \$buffer) or die "Opening buffer failed: $!";
$inUpath++;
}
print $currentOut '<', $element->{Name};
print $currentOut attributes($element->{Attributes});
print $currentOut '>';
}
sub end_element {
my ($self, $element) = @_;
print $currentOut '</', $element->{Name};
print $currentOut '>';
if ('userinterface' eq $element->{Name}) {
$inUserinterface--;
}
if ('upath' eq $element->{Name}) {
close($currentOut);
$currentOut = $defaultOut;
if ($inUserinterface && $inUpath) {
if (!exists $seen{lc($buffer)}) {
print $currentOut $buffer;
} else {
$ret{skipped}++;
$ret{cut} .= $buffer;
}
$seen{lc($buffer)} = 1;
}
$inUpath--;
}
}
sub characters {
my ($self, $characters) = @_;
print $currentOut $characters->{Data};
}
sub attributes {
my ($attributesRef) = @_;
my %attributes = %$attributesRef;
foreach my $a (values %attributes) {
my $v = $a->{Value};
$v =~ s/&/&/g;
$v =~ s/</</g;
$v =~ s/>/>/g;
$v =~ s/"/"/g;
print $currentOut ' ', $a->{Name}, '="', $v, '"';
}
}
__DATA__
<package>
<id>1523456789</id>
<models>
<model type="A">
<start>2016-04-20</start>
<end>2017-04-20</end>
</model>
<model type="B">
<start>2016-04-20</start>
<end>2017-04-20</end>
</model>
</models>
<userinterface>
<upath>/Example/Dir/Here</upath>
<upath>/Example/Dir/Here2</upath>
<upath>/example/dir/here</upath>
</userinterface>
<userinterface>
<upath>/Example/Dir/<b>Here</b></upath> <upath>/Example/Dir/Here2</upath>
<upath>/example/dir/<b>here</b></upath>
</userinterface>
</package>
这个程序不再按行工作,而是查找userinterface
标签内的upath
标签,如果它们在父组中是重复的,则将其删除。周围的缩进和换行符保留。如果upath
标签内有upath
标签,那么情况可能会变得有点奇怪。
它看起来像这样:
$ perl saxEG.pl
<package>
<id>1523456789</id>
<models>
<model type="A">
<start>2016-04-20</start>
<end>2017-04-20</end>
</model>
<model type="B">
<start>2016-04-20</start>
<end>2017-04-20</end>
</model>
</models>
<userinterface>
<upath>/Example/Dir/Here</upath>
<upath>/Example/Dir/Here2</upath>
</userinterface>
<userinterface>
<upath>/Example/Dir/<b>Here</b></upath> <upath>/Example/Dir/Here2</upath>
</userinterface>
</package>
Duplicates skipped: 2
Duplicates cut: <upath>/example/dir/here</upath><upath>/example/dir/<b>here</b></upath>
<start>2016-04-20</start>
和<end>2017-04-20</end>
在文件中各出现了两次。 - ThisSuitIsBlackNot