Perl regex删除XML中的节点

Perl regex删除XML中的节点,xml,regex,perl,Xml,Regex,Perl,我有一个大的(>2gb)XML文件,大致如下所示: <record id="1"> <a> <detail>blah</detail> .... <detail>blah</detail> </a> <b> <detail>blah</detail> ....

我有一个大的(>2gb)XML文件,大致如下所示:

<record id="1">
    <a>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </a>
    <b>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </b>
    <c>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </c>
</record>
...
<record id="999999">
    <a>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </a>
    <b>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </b>
    <c>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </c>
</record>
<record id="1">
    <a>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </a>
    <c>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </c>
</record>
...
<record id="999999">
    <a>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </a>
    <c>
        <detail>blah</detail>
        ....
        <detail>blah</detail>
    </c>
</record>
但这不起作用-它根本没有删除任何节点

我确信
节点没有任何会破坏模式的属性

很明显,在这个问题上,我是个傻瓜,所以我肯定我还没有接近……

((?!))*)
<b>(((?!<\/b>).)*)<\/b>
试试这个,换成

见演示

可用于从大型XML文件中剪切元素,而无需担心元素之间的空白:

use warnings;
use strict;
use XML::Twig;

my $xml = do { local $/; <DATA> };

my $twig = XML::Twig->new(
    twig_handlers => {
        'record/b' => sub { $_->cut() }
    },
    pretty_print => 'indented'
);
$twig->parse($xml);
$twig->print();

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<top>
    <record id="1">
        <a>
            <detail>blah</detail>
            <detail>blah</detail>
        </a>
        <b>
            <detail>blah</detail>
            <detail>blah</detail>
        </b>
        <c>
            <detail>blah</detail>
            <detail>blah</detail>
        </c>
    </record>
    <record id="999999">
        <a>
            <detail>blah</detail>
            <detail>blah</detail>
        </a>
        <b>
            <detail>blah</detail>
            <detail>blah</detail>
        </b>
        <c>
            <detail>blah</detail>
            <detail>blah</detail>
        </c>
    </record>
</top>
使用警告;
严格使用;
使用XML::Twig;
my$xml=do{local$/;};
my$twig=XML::twig->new(
细枝处理程序=>{
'record/b'=>sub{$\u->cut()}
},
漂亮的打印=>“缩进”
);
$twig->parse($xml);
$twig->print();
__资料__
废话
废话
废话
废话
废话
废话
废话
废话
废话
废话
废话
废话
以下是输出:

<?xml version="1.0" encoding="UTF-8"?>
<top>
  <record id="1">
    <a>
      <detail>blah</detail>
      <detail>blah</detail>
    </a>
    <c>
      <detail>blah</detail>
      <detail>blah</detail>
    </c>
  </record>
  <record id="999999">
    <a>
      <detail>blah</detail>
      <detail>blah</detail>
    </a>
    <c>
      <detail>blah</detail>
      <detail>blah</detail>
    </c>
  </record>
</top>

废话
废话
废话
废话
废话
废话
废话
废话

您可以使用此正则表达式:

<b>[\s\S]+?<\/b>
[\s\s]+?

其思想是删除您可以使用的libxml2 pull解析器的
标记:

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML::Reader;

my $r = 'XML::LibXML::Reader'->new( location => 'file.xml' );
while ($r->nextElement('record')) {
    my $rec = $r->copyCurrentNode(1);
    for my $del ($rec->findnodes('b')) {
        $rec->removeChild($del);
    }
    print $rec;
}

我建议使用实际的XML解析器,如。
s@.*@@sg
应该从第一个
删除到最后一个
。它没有移除任何东西?可能是命令行Perl选项。不过,通常在这种情况下,您会使用惰性量词,如
s@.*?@@sg
即使这样做,格式也会被破坏。请确保设置了“点所有”修饰符。我想,您也可以使用xml::Twig附带的xml_grep(现在对我来说测试它太晚了)考虑到文件的大小(~2GB),这个
xml\u grep-v'record/b'file.xml>new\u file.xml
会起作用,这样说合适吗?@miller是的,您正在内存中读取整个文件,实际上只需执行
xml::Twig->new(Twig\u root=>{'record/b'=>1},Twig\u print\u out\u root=>1)->parsefile(“file.xml”)
将输出整个文件(因为
在根之外打印
,而忽略
b
元素)。教程的第4.5节介绍了这一点。为什么
[\s\s]
而不是
(使用regexp的
s
修饰符)?
#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML::Reader;

my $r = 'XML::LibXML::Reader'->new( location => 'file.xml' );
while ($r->nextElement('record')) {
    my $rec = $r->copyCurrentNode(1);
    for my $del ($rec->findnodes('b')) {
        $rec->removeChild($del);
    }
    print $rec;
}