首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >修正不带id的xml文件的多行分隔

修正不带id的xml文件的多行分隔
EN

Stack Overflow用户
提问于 2022-04-06 08:39:09
回答 1查看 110关注 0票数 0

我有一个外部生成的大型xml文件,其中包含一些无效字符,在我的例子中是一个反斜杠。我知道用什么替换这些字段,这样我就可以编辑一个文件并手动修复它。然而,有许多这样的文件,都有相同的问题。我想写一个bash脚本来修复它们。

Problem问题部分如下所示。

代码语言:javascript
复制
<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>

期望输出

代码语言:javascript
复制
<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“

代码语言:javascript
复制
#!/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

至于如何更有效地进行这项工作,我亦乐意听取任何意见。谢谢。

编辑的请求,我已经添加了一个例子,是更有代表性的完整文件我正在处理。

代码语言:javascript
复制
<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方面没有经验,这就是为什么我可能忽略了一些显而易见的解决方案。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-06 18:17:46

使用适当的XML解析器。

使用XML::LibXML,单向

代码语言:javascript
复制
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'

代码语言:javascript
复制
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,单向

代码语言:javascript
复制
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节点之后),一种方法是先在这些节点之后添加,而我们仍然可以标识这些位置,然后才能删除它们。

代码语言:javascript
复制
# 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} } );

使用这个库,如果需要替换节点的内容也很容易(而不是添加和删除)。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71763566

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档