2012-03-26 4 views
0

プロセスの1つに、ExcelスプレッドシートからOxygenエディタ文書へのコピー・ペーストがあります。それはかなりうまくいくが、特殊文字を捕まえないので、私はそれらを見つけて変更するスクリプトを書いている。私はストリームモードで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表記の直接的な使用と、要素の複数の属性が順番に保持されていることに注意してください。

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; 
01の構造のすべてのテキスト・ノードを変更するデモンストレーションであります

出力

<?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

+0

@Greg:問題は表示されません。 '// text()'を使って、XML文書内のすべてのPCDATAにアクセスできます。すべてのテキストノードを小文字に設定する方法を示すために、私の答えに追加しました。 – Borodin

+0

私はXPathが再びそれを見て前に私を助けることができないと宣言しました。 (私はそれを勉強して使ってから何年も経っています)。 – Greg

関連する問題