我有一个外部生成的大型xml文件,其中包含一些无效字符,在我的例子中是一个反斜杠。我知道用什么替换这些字段,这样我就可以编辑一个文件并手动修复它。然而,有许多这样的文件,都有相同的问题。我想写一个bash脚本来修复它们。
Problem问题部分如下所示。
<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至于如何更有效地进行这项工作,我亦乐意听取任何意见。谢谢。
编辑的请求,我已经添加了一个例子,是更有代表性的完整文件我正在处理。
<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方面没有经验,这就是为什么我可能忽略了一些显而易见的解决方案。
发布于 2022-04-06 18:17:46
使用适当的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://stackoverflow.com/questions/71763566
复制相似问题