Perl:标记分隔字符串中的全局替换

时间:2010-08-01 11:32:31

标签: xml perl replace tags substitution

我的目标是将所有尾随实例替换为标记括号内的尾随+。让我们假设要替换的行如下所示:

<h> aa- aa- </h> <h> ba- ba- </h> 

然后应该看起来像

<h> aa+ aa+ </h> <h> ba+ ba+ </h>

首先我尝试了这个表达式:

s/<h>(.*?)-(.*?)<\/h>/<h>$1+$2<\/h>/g;

产生了这个输出:

<h> aa+ aa- </h> <h> ba+ ba- </h>

g选项确实会导致每行有多个替换,但仅限于每个标记括号的第一个实例(并且仅当两个圆括号都包含问号时)。

为了缩小问题范围,我接着尝试了无视标签来实现替换。表达式

s/(.*?)-(.*?)/$1+$2/g; 

确实导致了期望的结果

<h> aa+ aa+ </h> <h> ba+ ba+ </h>

当然,这也会替换标签括号以外的内容。

那么我的第一个表达式出现了什么问题,如何在标记括号内实现完全替换的目标?

2 个答案:

答案 0 :(得分:1)

由于您使用正则表达式解析XML(在一般情况下不是一个好主意),我假设您愿意对您的输入做出一些假设。如果是这样,以下替换可能就足够了。

它用加号替换减号,前提是减号是:(a)在单词边界,(b)后跟一些可选的非左角括号文本,然后是关闭标记。如果我们可以假设有效的文档,则无需担心开始标记。第二个条件是使用前瞻断言强制执行,以便正则表达式不会消耗字符串,允许您替换所有这些减号。

s/ \b- (?= [^<]* <\/h>) /+/xg;

另一个选择是运行你的正则表达式,直到它无法替换任何东西。在标量上下文中,全局替换返回所做的替换次数,这可以作为您何时停止处理行的测试:

my $n = 1;
$n = s/YOUR_REGEX/YOUR_REPLACE/g while $n;

答案 1 :(得分:0)

这是一种方法:将字符串拆分为标记位和非标记位,并仅对标记位执行替换。

$_ = join("", map { if(/^<h>/) { # if it's a tagged bit...
                        s/-($|\s|<)/+$1/g; # replace all trailing '-'s
                    }
                    $_}
                  split m!(<h>.*?</h>)!) # split into tagged and non-tagged bits