使用perl中的regex替换xml标记内的多行字符串

时间:2014-10-01 21:31:58

标签: regex perl

我一直试着让这个工作但没有运气。这是我的文本文件(first.txt)

<metric>
 <baseFilter>
  <and>
   <or>
    <value field="id">1111</value>
    <value field="id">2222</value>
   </or>
   <or>
    <value field="resolution" />
   </or>
</metric>

我想用第二个文本文件(second.txt)替换第一个“或”和“/或”之间的字符串。我可以在第一个“或”和“/或”之间有50个或更多的值字段行,因此,我在“或”和“/或”之间搜索字符串,并用second.txt中的任何内容替换。

<value field="id">3333</value>
<value field="id">4444</value>

预期输出:

<metric>
 <baseFilter>
  <and>
   <or>
    <value field="id">3333</value>
    <value field="id">4444</value>
   </or>
   <or>
    <value field="resolution" />
   </or>
</metric>

我已经获得了以下perl代码。

#!/usr/bin/perl

my $first = 'first.txt';
open (my $fh, '<', $first) or die "cannot open file $first";
{
  local $/;
  $first = <$fh>;
}

$find = "([\s]+)(<or>)([\n\r\s]+).*(\n|.)+?([\n\r\s]+)(<\/or>)";

my $content = 'second.txt';
open (my $fh, '<', $content) or die "cannot open file $content";
{
 local $/;
 $content = <$fh>;
}

$first =~ s/$find/$1$2$3$content$5$6/;
print "After sub First is $first\n\n";

当我运行我的代码时,替换没有发生,我的$ first保持不变,即first.txt再次出现。我错过了什么?我在像http://www.regexr.com/这样的在线正则表达式测试器中使用了我的正则表达式,我的正则表达式匹配第一个“或”和“/或”之间的多行字符串。 为什么perl不喜欢我的正则表达式?

3 个答案:

答案 0 :(得分:2)

通过尝试捕获所有这些XML片段,您在比赛中过于复杂。以下正则表达式是一种更简单的替换方法:

$first =~ s#(<or>\s+)<value field="id">.*?</value>(\s*</or>)#$1$content$2#sm;

我使用了修饰符sm,它们允许匹配多行,并允许.包含换行符;因此,我们可以替换<or>开始和结束标记之间的任意数量的行。我还使用#作为我的正则表达式的分隔符,所以我没有必须逃避XML关闭标记中的斜杠。

有关正则表达式的更多信息,请参阅perlre,特别是有关修饰符的信息。

答案 1 :(得分:0)

与往常一样,使用正则表达式操作XMNL是一个非常糟糕的想法。因此,您可以看到执行操作是多么简单&#34;正确&#34;,此程序使用XML::LibXML模块执行您所要求的操作。

  • 创建XML解析器对象并用于解析second.xml文件的每一行,将它们放入@fragments数组中以供以后使用

  • 解析first.xml文件,findnodes找到所有or个元素,其中第一个元素用removeChildNodes清空,并再次填充每一行来自@fragments使用appendChild

  • 最后,使用toString格式化XML并打印

use strict;
use warnings;
use 5.010;
use autodie;

use XML::LibXML;

my $parser = XML::LibXML->new(no_blanks => 1);

open my $fh, '<', 'second.xml';
my @fragments = map {
   chomp;
   $parser->parse_balanced_chunk($_);
} <$fh>;
close $fh;

my $xml = $parser->load_xml(location => 'first.xml');

my @or_nodes = $xml->findnodes('//or');
$or_nodes[0]->removeChildNodes;
$or_nodes[0]->appendChild($_) for @fragments;

print $xml->toString(1);

<强>输出

<?xml version="1.0"?>
<metric>
  <baseFilter>
    <and>
      <or>
        <value field="id">3333</value>
        <value field="id">4444</value>
      </or>
      <or>
        <value field="resolution"/>
      </or>
    </and>
  </baseFilter>
</metric>

答案 2 :(得分:0)

首先将新值加载到数组中。

然后使用$INPLACE_EDIT使用如下逻辑编辑文件:

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

my @newvals = qw(3333 4444);

while (<DATA>) {
    s{<value field="id">\K\w+(?=</value>)}{shift @newvals}e if @newvals;
    print;
}

__DATA__
<metric>
 <baseFilter>
  <and>
   <or>
    <value field="id">1111</value>
    <value field="id">2222</value>
   </or>
   <or>
    <value field="resolution" />
   </or>
</metric>

输出:

<metric>
 <baseFilter>
  <and>
   <or>
    <value field="id">3333</value>
    <value field="id">4444</value>
   </or>
   <or>
    <value field="resolution" />
   </or>
</metric>