2012-03-26 77 views
0

我们的一个流程包括将Excel电子表格复制粘贴到氧气编辑器文档中。它工作得很好,但不抓特殊字符,所以,我正在编写一个脚本来查找和更改它们。我已经开始在流模式下使用XML :: Parser,但是我不太确定我将如何使用这种方法获得我需要的地方。尝试编辑XML文档中的PCDATA

首先,因为解析器(正确)不关心属性顺序,所以属性可以(并且)以不同顺序返回,这会使一些人烦恼。另外,我目前还不能一致地识别PCDATA。而且,重新组装元素标签似乎有点多...而且我也不会很好地处理EMPTY元素。我在这里只是想念一下,还是应该看看别的东西,比如XML :: Twig?

在此先感谢所有需要回复的人(任何人)!

use strict; 
use warnings; 
use IO::File; 
use XML::Parser; 

my $xml = <<EOD; 
<?xml version="1.0"?> 
<messages> 
    <message> 
    <from id="t_8ur9k0" type="king">Maximus</from> 
    <to>knave</to> 
    <subject>My boots</subject> 
    <body>I <i>really</i> want my riding boots. Bring them to me, at once!</body> 
    </message> 
</messages> 
EOD 

my $parser = new XML::Parser(Style => 'Stream', ErrorContext => 2); 
$parser->setHandlers(Start => \&handle_start, 
     End => \&handle_end, 
     Char => \&handle_char, 
     Default => \&handle_default); 

$parser->parse($xml); 

sub handle_start { 
    my ($self, $tag, %attrs) = @_; 
    my $atts = ''; 
    if (%attrs) { 
    while (my ($key, $val) = each(%attrs)) { 
     $atts .= " " . $key . '="' . $val . '"'; 
    } 
    } 
    print "<" . $tag . $atts . ">"; 
} 

sub handle_end { 
    my ($self, $tag) = @_; 
    print "</" . $tag . ">"; 
} 

sub handle_char { 
    my ($self,$raw) = @_; 
    if (!($raw =~ m/\s/)) { 
    $raw =~ s/.*/FOO/; 
    } 
    print $raw; 
} 

sub handle_default { 
    my ($self,$str) = @_; 
    print $str; 
} 

回答

0

XML::Parser提供的属性信息以正确的顺序Start回调处理程序。属性出现在您的程序中的顺序不正确,因为您将它们放入行中的散列表中

my ($self, $tag, %attrs) = @_; 

它失去了排序。

XML::Parser很少单独使用。您可以使用XML::Twig,但我的首选是XML::LibXML

你没有说你想对数据做什么样的转换,但是这个程序再现了除了删除的(任意选择的)<subject>元素之外的输入。请注意直接使用XPath表示法来操作文档,以及<from>元素的多个属性保持有序。

use strict; 
use warnings; 

use XML::LibXML; 

my $doc = XML::LibXML->load_xml(string => <<XML); 
<?xml version="1.0"?> 
<messages> 
    <message> 
    <from id="t_8ur9k0" type="king" b="b" c="c" d="d" e="e" f="f" g="g">Maximus </from> 
    <to>knave</to> 
    <subject>My boots</subject> 
    <body>I <i>really</i> want my riding boots. Bring them to me, at once!</body> 
    </message> 
</messages> 
XML 

my @nodes = $doc->findnodes('/messages/message/subject'); 
$nodes[0]->unbindNode; 
print $doc->toString; 

输出

<?xml version="1.0"?> 
<messages> 
    <message> 
    <from id="t_8ur9k0" type="king" b="b" c="c" d="d" e="e" f="f" g="g">Maximus </from> 
    <to>knave</to> 

    <body>I <i>really</i> want my riding boots. Bring them to me, at once!</body> 
    </message> 
</messages> 

UPDATE

这是修改所有文本节点的示范无论文档的结构的

use strict; 
use warnings; 

use XML::LibXML; 

my $doc = XML::LibXML->load_xml(string => <<XML); 
<?xml version="1.0"?> 
<messages> 
    <message> 
    <from id="t_8ur9k0" type="king" b="b" c="c" d="d" e="e" f="f" g="g">Maximus </from> 
    <to>knave</to> 
    <subject>My boots</subject> 
    <body>I <i>really</i> want my riding boots. Bring them to me, at once!</body> 
    </message> 
</messages> 
XML 

my @nodes = $doc->findnodes('//text()'); 
$_->setData(lc $_->data) for @nodes; 
print $doc->toString; 

输出

<?xml version="1.0"?> 
<messages> 
    <message> 
    <from id="t_8ur9k0" type="king" b="b" c="c" d="d" e="e" f="f" g="g">maximus </from> 
    <to>knave</to> 
    <subject>my boots</subject> 
    <body>i <i>really</i> want my riding boots. bring them to me, at once!</body> 
    </message> 
</messages> 
+0

啊,OK。我记得,XML解析器不需要遵守属性顺序,所以我认为这就是抛弃它们的原因。哈希也是这样做的。并且对于我之后不清楚的道歉:我想扫描PCDATA元素的内容并将特殊字符更改为实体;例如,将每个[±]更改为[±]。所以XPath将无法维护,每次更改DTD都要求我更新脚本。 – Greg 2012-03-27 11:58:33

+0

@Greg:我没有看到问题。您可以使用'// text()'访问XML文档中的所有PCDATA。我已添加到我的答案中,以显示如何将所有文本节点设置为小写。 – Borodin 2012-03-27 14:28:56

+0

我确实 - 我宣布XPath无法在再次查看之前帮助我。 (我学习和使用它已经有多年了。)我很抱歉打扰! – Greg 2012-03-27 15:05:14