2

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

<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>

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

那么我的第一个表达式有什么问题,我怎样才能实现在标签括号内完全替换的目标?

4

2 回答 2

1

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

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

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

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

my $n = 1;
$n = s/YOUR_REGEX/YOUR_REPLACE/g while $n;
于 2010-08-01T13:17:14.867 回答
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
于 2010-08-01T12:13:00.910 回答