我有一個大型外部生成的 xml 檔案,其中包含一些無效字符,在我的情況下是反斜杠。我知道用什么來替換這些欄位,所以我可以 gedit 一個檔案并手動修復它。然而,有許多這樣的檔案,都有同樣的問題。我想撰寫一個 bash 腳本來修復它們。
問題 有問題的部分如下所示。
<root>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> \00\00\00 </field>
<field> \00\00\00 </field>
<field> \00\00\00 </field>
<set>
All the data
</set>
</array>
</root>
期望的輸出
<root>
<array>
<dimension> dim="1">gridpoints</dimension>
<dimension> dim="2">morepoints</dimension>
<dimension> dim="3">evenmorepoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> d </field>
<field> e </field>
<field> f </field>
<set>
All the data
</set>
</array>
</root>
修復到目前為止,我已經找到了一種使用 perl 洗掉有問題的反斜杠的方法,但是我不知道如何單獨編輯欄位,因為下面的代碼得到了所需的解決方案,但是每個欄位都有條目“a”
#!/bin/bash
perl -CSDA -pe'
s/[^\x9\xA\xD\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}] //g;
' file.xml > temp.xml
xmlstarlet ed -u "/root/array/field" -v "a" temp.xml > file_fixed.xml
我也很樂意就如何更有效地做到這一點提出任何建議。謝謝你。
編輯按照 zdim 的要求,我添加了一個更能代表我正在處理的完整檔案的示例。
<root>
<path1>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> a </field>
<field> b </field>
<field> c </field>
<field> \00\00\00 </field>
<field> \00\00\00 </field>
<field> \00\00\00 </field>
<set>
All the data
</set>
</array>
</path1>
<path2>
<array>
<dimension> dim="1">gridpoints</dimension>
<field> Behaves Correctly </field>
</array>
</path2>
</root>
應該注意的是,我從另一個程式接收這些檔案作為輸出,然后需要在將它們輸入下一個程式之前修復它們。我對 xml 幾乎沒有經驗,這就是為什么我可能錯過了一些明顯的解決方案。
uj5u.com熱心網友回復:
使用適當的 XML 決議器。
使用XML::LibXML,一種方式
use warnings;
use strict;
use feature 'say';
use XML::LibXML;
my $filename = shift // die "Usage: $0 file.xml\n"; #/ fix syntax hilite
my $doc = XML::LibXML->load_xml(location => $filename);
# Remove unwanted nodes
foreach my $node ($doc->findnodes('//field')) {
#say $node->toString;
if ($node->toString =~ m{\\00\\00\\00}) {
say "Removing $node";
$node->parentNode->removeChild($node);
}
}
# Add desired new nodes (right after the last <field> node)
my $last_field_node = ( $doc->findnodes('//field') )[-1];
my $field_node_name = $last_field_node->nodeName;
my $parent = $last_field_node->parentNode;
for ("E".."F") {
my $new_elem = $doc->createElement( $field_node_name );
$new_elem->appendText($_);
$parent->insertAfter($new_elem, $last_field_node);
}
# Add other nodes (like the mentioned "dimension") the same way
print $doc->toString;
如示例中所示,我使用基本的正則運算式來識別要洗掉的模式。請根據您的實際輸入調整代碼。
這會在最后一個節點之后添加新<field>節點。但是如果我們需要在洗掉的節點之后添加,雖然可能還有更多<field>的節點,那么首先在<field>需要洗掉的最后一個節點之后添加,然后才洗掉它們。
或者,也許您只需將<field>節點的內容替換為'\00\00\00'
my @replacements = "AA" .. "ZZ"; # li'l list of token replacements
foreach my $node ($doc->findnodes('//field')) {
if ($node->toString =~ m{\\00\\00\\00}) {
say "Change $node -- remove child (text) nodes, add new";
$node->removeChildNodes;
$node->appendText(shift @replacements);
}
}
一個元素的“值”實際上是一個文本節點,它有一個值。與其直接替換該(文本子節點的)值,不如洗掉(所有)元素的(文本)子節點,然后添加所需的新節點。
然后,此代碼會處理\00\00\00是否需要簡單地替換它們,從一些替換串列中提取。要添加<dimension>節點,請insertAfter按上述方式使用。
有一些更漂亮的列印模塊,例如XML::LibXML::PrettyPrint
使用Mojo::DOM,一種方式
use warnings;
use strict;
use feature 'say';
use Path::Tiny; # convenience, for "slurp"-ing a file
use Mojo::DOM;
my $filename = shift // die "Usage: $0 file.xml\n"; #/ fix syntax hilite
my $dom = Mojo::DOM->new( path($filename)->slurp );
# my $dom = Mojo::DOM->new->xml(1)->parse(path($filename)->slurp);
# Remove unwanted, by filtering them first
$dom->find("field")
-> grep( sub { $_->text =~ m{\\00\\00\\00} } )
-> each( sub { $_[0]->remove } );
# Or directly while iterating
# $dom->find("field")->each(
# sub { $_[0]->remove if $_[0]->text =~ m{\\00} } );
# Add new ones, after last 'field'
foreach my $content ("E".."F") {
my $tag = $dom->new_tag('field', $content);
$dom->find('field')->last->append($tag);
}
say $dom;
再次,請根據實際檔案結構進行調整。
一個例子。如果field需要在field要洗掉的節點之后立即添加新節點(而不是在其他一些field節點之后),一種方法是先在這些節點之后添加,而我們仍然可以識別這些位置,然后才洗掉它們。
# Add new ones, after last 'field' that has \00\00\00 text in it
foreach my $content ("E".."F") {
my $tag = $dom->new_tag('field', $content);
$dom->find('field')->grep(sub { m{\\00\\00\\00} })->last->append($tag);
}
# Only now remove those 'field' nodes with \00\00\00
$dom->find("field")->each(
sub { $_[0]->remove if $_[0] =~ m{\\00\\00\\00} } );
有了這個庫,如果需要的話,也可以很容易地替換節點的內容(而不是添加和洗掉)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/464671.html
標籤:xml 重击 perl xmlstarlet
