我需要使用正則運算式按屬性搜索。
在 Python 中,它看起來像這樣:
from lxml import etree
dom = etree.parse(r'/path/to/file.XML')
regexpNS = "http://exslt.org/regular-expressions"
els = dom.xpath("//*[(re:test(@NAME, '.*Town.*', 'i')) and (@ISACTIVE='1' )]", namespaces={'re':regexpNS})
el = els[0]
print(el.attrib['NAME'] " => " el.attrib['OBJECTGUID'])
我不明白如何在 perl 中做到這一點
my $dom = XML::LibXML->new->parse_file("/path/to/file.XML");
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs('re', 'http://exslt.org/regular-expressions');
print $xpc->findnodes(q{//*[(re:test(@NAME, '.*Town.*', 'i')) and (@ISACTIVE='1' )]});
給出錯誤資訊
錯誤:xmlXPathCompOpEval:未找到函式測驗 XPath 錯誤:
未注冊的功能在行...
我試圖重寫眾所周知的例子:
自定義 XPath 函式
這個例子通過定義一個基于 Perl 正則運算式過濾節點的函式來演示 registerFunction() 方法:
sub grep_nodes {
my ($nodelist,$regexp) = @_;
my $result = XML::LibXML::NodeList->new;
for my $node ($nodelist->get_nodelist()) {
$result->push($node) if $node->textContent =~ $regexp;
}
return $result;
};
my $xc = XML::LibXML::XPathContext->new($node);
$xc->registerFunction('grep_nodes', \&grep_nodes);
my @nodes = $xc->findnodes('//section[grep_nodes(para,"\bsearch(ing|es)?\b")]');
改寫成這樣:
use XML::LibXML;
my $dom = XML::LibXML->new->parse_string(<<'EOT');
<?xml version="1.0" encoding="utf-8"?>
<ADDRESSOBJECTS>
<OBJECT ID="1" NAME="Broadway" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="2" NAME="Times Square" TYPENAME="sq" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="3" NAME="DownTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="4" NAME="MidthTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="5" NAME="UpTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
</ADDRESSOBJECTS>
EOT
sub grep_attrs {
my ($nodelist,$attr_name,$regexp) = @_;
my $result = XML::LibXML::NodeList->new;
for my $node ($nodelist->get_nodelist()) {
my %attrs = map { $_->getName => $_->getValue } $node->attributes;
$result->push($node) if $attrs{$attr_name} =~ $regexp;
print $attrs{$attr_name}."\n" if $attrs{$attr_name} =~ $regexp;
}
return $result;
};
print "\n-========================================-\n";
my $xc = XML::LibXML::XPathContext->new($dom);
$xc->registerFunction('grep_attrs', \&grep_attrs);
my @nodes = $xc->findnodes(q{//*[grep_attrs(OBJECT,'NAME','.*Town.*')]});
print "\n-========================================-\n";
print @nodes;
print "\n-========================================-\n";
輸出結果:
-========================================-
DownTown
MidthTown
UpTown
-========================================-
<ADDRESSOBJECTS>
<OBJECT ID="1" NAME="Broadway" TYPENAME="st" LEVEL="8" ISACTIVE="1"/>
<OBJECT ID="2" NAME="Times Square" TYPENAME="sq" LEVEL="8" ISACTIVE="1"/>
<OBJECT ID="3" NAME="DownTown" TYPENAME="st" LEVEL="8" ISACTIVE="1"/>
<OBJECT ID="4" NAME="MidthTown" TYPENAME="st" LEVEL="8" ISACTIVE="1"/>
<OBJECT ID="5" NAME="UpTown" TYPENAME="st" LEVEL="8" ISACTIVE="1"/>
</ADDRESSOBJECTS>
-========================================-
功能有效,但是!
- 太長了,比 python 長很多倍
- 由于某種原因,它回傳完整的樹,而不是找到的節點。
幫助我理解問題以及在按屬性搜索時如何使用正則運算式?
uj5u.com熱心網友回復:
使用XML::XPath相反,它通過 XPath 2.0 函式在幾個版本之前支持正則運算式,這matches()要感謝你的:
#!/usr/bin/env perl
use warnings;
use strict;
use feature qw/say/;
use XML::XPath v1.45;
my $xml = <<'EOXML';
<?xml version="1.0" encoding="utf-8"?>
<ADDRESSOBJECTS>
<OBJECT ID="1" NAME="Broadway" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="2" NAME="Times Square" TYPENAME="sq" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="3" NAME="DownTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="4" NAME="MidthTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="5" NAME="UpTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
</ADDRESSOBJECTS>
EOXML
my $xp = XML::XPath->new(xml => $xml);
my @nodes = $xp->findnodes(q{//*[matches(@NAME, 'town', 'i') and @ISACTIVE = 1]});
for my $node (@nodes) {
say $node->getAttribute('NAME');
}
列印出來
DownTown
MidthTown
UpTown
uj5u.com熱心網友回復:
使用 XML::LibXML 模塊和注冊函式的解決方案:
#!/usr/bin/env perl -CSDA
use utf8;
use warnings;
use strict;
use feature qw/say/;
use XML::LibXML;
my $xml = <<'EOXML';
<?xml version="1.0" encoding="utf-8"?>
<ADDRESSOBJECTS>
<OBJECT ID="1" NAME="Broadway" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="2" NAME="Times Square" TYPENAME="sq" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="3" NAME="DownTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="4" NAME="MidthTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
<OBJECT ID="5" NAME="UpTown" TYPENAME="st" LEVEL="8" ISACTIVE="1" />
</ADDRESSOBJECTS>
EOXML
my $dom = XML::LibXML->new->parse_string($xml);
sub xpath_matches {
my ($input,$pattern,$flg) = @_;
$flg = '' if !defined ($flg);
return 1 if $input =~ /(?$flg)$pattern/;
return undef;
}
my $xc = XML::LibXML::XPathContext->new($dom);
$xc->registerFunction('matches', \&xpath_matches);
say $_->getAttribute('NAME').' '.$_->getAttribute('TYPENAME')
for $xc->findnodes(q{
//OBJECT[@NAME and matches(@NAME,'.*[tT]oWn$','i')]
});
PS:因為perl中沒有布林值,而matches是一個回傳某個值的函式,那么它的結果必須和某個值進行比較,在這種情況下是1,如果XPATH查詢中只使用一個函式,見例子:
//OBJECT[matches(@NAME,'.*[tT]oWn$','i')=1]
如果 XPATH 查詢中還有一些其他的操作,那么可以不用比較直接使用,如上例
//OBJECT[@NAME and matches(@NAME,'.*[tT]oWn$','i')]
或者
//OBJECT[matches(@NAME,'.*[tT]oWn$','i') and 1]
在處理非常大的 XML 檔案時,此解決方案的速度幾乎比 XML::XPath 快 15 倍。在數百兆位元組的檔案上,XML::XPath 就這樣消失了。
所要做的就是正確撰寫和使用注冊函式。
所以可以使用 XML::LibXML 中 XPATH 中的正則運算式!
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/506253.html
標籤:xml perl 路径 libxml2 xpath-2.0
