考虑以下输入:
<Foo
Bar="bar"
Baz="1"
Bax="bax"
>
处理完毕后,我需要它看起来如下:
<Foo
Bar="bar"
Baz="1"
Bax="bax"
CustomAttribute="TRUE"
>
这是所有我需要做的不超过5个文件,所以使用除正则表达式之外的任何东西似乎有点矫枉过正。无论如何,我想出了以下(Perl)正则表达式来实现这个目标:
$data =~ s/(<\s*Foo)(.*?)>/$1$2 CustomAttribute="TRUE">/sig;
这很有效,但是,有一个明显的问题。这种模式是“愚蠢的”,因为如果已添加CustomAttribute
,上面列出的操作将只是盲目追加另一个CustomAttribute=...
。
当然,一个简单的解决方案是编写一个辅助表达式,在运行替换操作之前尝试匹配CustomAttribute
。
由于我对脚本语言和正则表达式世界都不熟悉,我想知道是否可以在不引入任何宿主语言结构(即Perl中的if语句)的情况下解决此问题,并且只需使用我上面写的更“智能”的版本?
答案 0 :(得分:5)
我不会因为你不应该使用正则表达式来打败你。我的意思是,你不应该这样做,但是你明显知道你在问题中所说的话,所以继续...
可以完成你所要求的东西被称为negative lookahead assertion(通常是(?!...)
),基本上说如果断言中的模式是你的,你不希望匹配应用在此之前找到了。在您的示例中,如果CustomAttribute
已经存在,则不希望它应用,因此:
$data =~ s/(<\s*Foo)(?![^>]*\bCustomAttribute=)(.*?)>/$1$2CustomAttribute="TRUE">/sig;
答案 1 :(得分:5)
这听起来像是XML::Twig的工作,它可以处理XML并在其中运行时更改部分内容,包括向标记添加属性。我怀疑你花了很多时间习惯Twig,你会发现一个只有大部分工作的正则表达式解决方案。而且,最后你会知道足够的Twig在下一个项目中使用它。 :)
答案 2 :(得分:4)
我猜演讲的时间; - )
我不确定为什么你认为使用一个完整的XML处理器是过度的。使用适当的工具编写代码实际上更容易。正则表达式将更复杂,并将依赖于对数据的不成文假设,这是危险的。其中一些假设很可能是:没有'&gt;'在属性值中,没有CDATA部分,标签或属性名称中没有非ascii字符,一致的属性值引用...
正则表达式唯一能给你的是保证输出保持数据的原始格式(在你的情况下,属性都是在一个单独的行上)。但是如果你的格式是可以完成的,那么它应该没关系,除非你把XML放在面向行的版本控制系统中。
以下是XML :: Twig的示例。它假设你有足够的内存来保存内存中的任何整个Foo
元素,它甚至可以在DATA部分中公认的XML设计中工作。使用XML :: LibXML可能同样容易(在内存中读取XML,选择所有Foo
元素,为每个元素添加属性,输出,这是我的计数中易于理解的5行)。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
my( $tag, $att, $val)= ( 'Foo', 'CustomAttribute', 'TRUE');
XML::Twig->new( # only process those elements
twig_roots => { $tag => sub {
# add/set attribute
$_->set_att( $att => $val);
# output and free memory
$_->flush;
}
},
twig_print_outside_roots => 1, # output everything else
pretty_print => 'cvs', # seems to be the right format
)
->parse( \*DATA) # use parsefile( $file) if parsing... a file
->flush; # not needed in XML::Twig 3.33
__DATA__
<doc>
<Foo
Bar="bar"
Baz="1"
Bax="bax"
>
here is some text
</Foo>
<Foo CustomAttribute="TRUE"><Foo no_att="1"/></Foo>
<bar><![CDATA[<Foo no_att="1">tricked?</Foo>]]></bar>
<Foo><![CDATA[<Foo no_att="1" CustomAttribute="TRUE">tricked?</Foo>]]></Foo>
<Foo
Bar=">"
Baz="1"
Bax="bax"
></Foo>
<Foo
Bar="
>"
Baz="1"
Bax="bax"
></Foo>
<Foo
Bar=">"
Baz="1"
Bax="bax"
CustomAttribute="TRUE"
></Foo>
<Foo
Bar="
>"
Baz="1"
Bax="b
ax"
CustomAttribute="TR
UE"
></Foo>
</doc>
答案 3 :(得分:1)
您可以通过带有'e'修饰符的函数发送匹配项以进行更多处理。
my $str = qq`
<Foo
Bar="bar"
Baz="1"
Bax="bax"
CustomAttribute="TRUE"
>
<Foo
Bar="bar"
Baz="1"
Bax="bax"
>
`;
sub foo {
my $guts = shift;
$guts .= qq` CustomAttribute="TRUE"` if $guts !~ m/CustomAttribute/;
return $guts;
}
$str =~ s/(<Foo )([^>]*)(>)/$1.foo($2).$3/xsge;