我有以下XML文件:
<d:entry id="a" d:title="a">
<d:index d:value="a" d:title="a"/>
<d:index d:value="b" d:title="b"/>
<d:index d:value="a" d:title="a"/>
<d:index d:value="c" d:title="c"/>
<d:index d:value="b" d:title="b"/>
<d:index d:value="a" d:title="a"/>
<d:index d:value="b" d:title="b"/>
<div>This is the content for entry.</div>
</d:entry>
<d:entry id="b" d:title="b">
<d:index d:value="a" d:title="a"/>
<d:index d:value="b" d:title="b"/>
<div>This is the content for entry.</div>
</d:entry>
(添加空格以提高可读性。)
<d:index
有一些重复项,我需要除去所有重复项,而只保留一个唯一的<d:index
。所需的效果是这样的:
<d:entry id="a" d:title="a">
<d:index d:value="a" d:title="a"/>
<d:index d:value="b" d:title="b"/>
<d:index d:value="c" d:title="c"/>
<div>This is the content for entry.</div>
</d:entry>
<d:entry id="b" d:title="b">
<d:index d:value="a" d:title="a"/>
<d:index d:value="b" d:title="b"/>
<div>This is the content for entry.</div>
</d:entry>
我可以为此目的在某些编辑器中进行正则表达式替换,但需要多次执行,我想知道Perl是否有某种方法可以一次运行。
答案 0 :(得分:3)
以下是过滤重复项的常用方法:
my @filtered = grep { !$seen{$_}++ } @unfiltered;
这可以满足您的需求,如以下代码片段所示:
my %seen;
for my $index_node ($xpc->findnodes('d:index', $entry_node)) {
my $value = $xpc->findvalue('@d:value', $index_node);
my $title = $xpc->findvalue('@d:title', $index_node);
if ($seen{$value}{$title}++) {
$index_node->unbind();
}
}
(我使用了首选的解析器XML :: LibXML,因为您没有提到您使用的是哪个解析器。)
答案 1 :(得分:2)
任何不了解XML的人都会告诉您不要使用正则表达式处理,而要使用适当的XML解析器和XML工具。如果您知道文件的格式将始终与显示的格式完全相同,则可以使用正则表达式(尽管不是我本人)来完成此操作,例如换行符,双引号和属性顺序完全与您的示例相同。但是,如果将其投入生产,那么生成XML的人将在一年后的StackOverflow上询问如何确保他们可以精确地以这种格式生成XML,因为如果属性顺序错误或使用单引号而不是双引号。因此,您正在为未来制造问题。 (请记住Postel的定律,在这种情况下,这意味着您应该接受任何等效于该XML的格式正确的XML。)
无论如何,在XSLT中执行此操作要比您提出的方法容易得多。假设您希望两个属性都匹配,以使元素计数为重复,则代码为:
<xsl:template match="d:entry">
<xsl:copy>
<xsl:for-each-group select="d:index"
group-by="concat(@d:value, '~', @d:title)">
<xsl:copy-of select="current-group()[1]"/>
</xsl:for-each-group>
<xsl:copy-of select="div"/>
</xsl:copy>
</xsl:template>
顺便说一句,您说“添加了空格以提高可读性”。该空格,特别是包含换行符的空格,将对任何正则表达式解决方案产生重大影响,但对正确编写的XSLT则完全没有影响。
答案 2 :(得分:2)
使用Mojo::DOM:
perl -MMojo::DOM -0777 -E'my $dom = Mojo::DOM->new->xml(1)->parse(<>);
$dom->find(q{d\\:entry})->each(sub { my %seen;
$_->find(q{d\\:index})->each(sub {
$_->remove if $seen{$_->{"d:value"}}{$_->{"d:title"}}++ }) });
print $dom->to_string' input.xml
结果:
<d:entry d:title="a" id="a">
<d:index d:title="a" d:value="a" />
<d:index d:title="b" d:value="b" />
<d:index d:title="c" d:value="c" />
<div>This is the content for entry.</div>
</d:entry>
<d:entry d:title="b" id="b">
<d:index d:title="a" d:value="a" />
<d:index d:title="b" d:value="b" />
<div>This is the content for entry.</div>
</d:entry>
-CS
开关执行此操作。