正则表达式替换文本但在文本位于特定标记之间时排除

时间:2012-09-19 10:44:01

标签: regex

我有以下字符串:

Lorem ipsum Test dolor sit amet, consetetur sadipscing elitr, sed diam nonumy <a href="http://Test.com/url">Test</a> eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd sed Test dolores et ea rebum. Stet clita kasd gubergren, no sea <a href="http://url.com">Test xyz</a> takimata sanctus est Lorem ipsum dolor sit amet.

现在我将替​​换标签之外的字符串'Test'而不是标签之间(例如替换为'1234')。

Lorem ipsum 1234 dolor sit amet, consetetur sadipscing elitr, sed diam nonumy <a href="http://Test.com/url">Test</a> eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd sed 1234 dolores et ea rebum. Stet clita kasd gubergren, no sea <a href="http://url.com">Test xyz</a> takimata sanctus est Lorem ipsum dolor sit amet.

我从这个正则表达式开始:(?!<a[^>]*>)(Test)([^<])(?!</a>)

但是有两个问题没有解决:

  1. 文本'Test'也会在Tags(例如<a href="http://Test.com/url">
  2. 中被替换
  3. 标签之间的文字与搜索到的文字不完全匹配,也会被替换(例如<a href="http://url">Test xyz</a>
  4. 我希望有人有解决方案来解决这个问题。

5 个答案:

答案 0 :(得分:10)

(?!<a[^>]*?>)(Test)(?![^<]*?</a>)

与zb226相同,但使用惰性匹配优化

此外,不建议在原始HTML上使用正则表达式。

答案 1 :(得分:9)

<强>答案

使用

(Test)(?!(.(?!<a))*</a>)

<强>解释

让我提醒你一些符号的含义:

1)?!negative lookahead,例如r(?!d)选择r之后没有直接后跟的所有d

enter image description here

2)因此,永远不要在没有角色的情况下开始消极的前瞻。只是(?!d)没有意义:

enter image description here

3)?可以用作懒惰匹配。例如,.+E将从

中进行选择
  

123EEE

整个字符串123EEE。但是,.+?E会根据需要选择“任何字符”(.+)。它只会选择123E

<强> 答案:

Protist的回答是你应该使用(?!<a[^>]*?>)(Test)(?![^<]*?</a>)。让我解释一下如何缩短它。

如2)所述,在比赛前进行前瞻是毫无意义的。所以以下相当于原始人的回答:

(Test)(?![^<]*?</a>)

由于不允许<,因此懒惰匹配?是多余的,因此它也等同于

(Test)(?![^<]*</a>)

这会选择所有Test之后没有</a>但后面没有符号<的{​​{1}}。这就是替换任何<a ...> .. </a>之前或之后出现的测试的原因。

但请注意

Lorem Test dolor <a href="http://Test.com/url">Test <strong>dolor</strong></a> eirmod

将更改为

Lorem 1234 dolor <a href="http://1234.com/url">1234 <strong>dolor</strong></a> eirmod 

为了抓住你可以改变你的正则表达式

(Test)(?!(.(?!<a))*</a>)

执行以下操作:

  

选择后面没有字符串Test的每个单词***</a>,其中***中的每个字符都不会跟<a

请注意,点.很重要(参见2))。

请注意,像(Test)(?!(.(?!<a))*?</a>)这样的惰性匹配不相关,因为嵌套链接在HTML4和HTML5中是非法的(像<a href="#">..<a href="#">...</a>..</a>)一样。

原始人说

  

此外,不建议在原始HTML上使用正则表达式。

我同意这一点。问题是如果标签未关闭或打开会导致问题。例如,这里提到的所有解决方案都会改变

Lorem Test dolor Test <strong>dolor</strong></a> eirmod

Lorem Test dolor Test <strong>dolor</strong></a> eirmod 1234 dolores sea 1234 takimata 

答案 2 :(得分:3)

这应该可以解决问题:

(<a[^>]*>)(Test)(?![^<]*</a>)

亲自试用on regexr.

答案 3 :(得分:2)

恢复这个古老的问题,因为它有一个没有提到的简单解决方案。

关于使用正则表达式解析html的所有免责声明,这是一种简单的方法。

Perl / PCRE的方法

<a[^>]*>[^<]*<\/a(*SKIP)(*F)|Test

demo

常规解决方案

<a[^>]*>[^<]*<\/a|(Test)

在此版本中,要替换的文本在组1中捕获,替换由简单的回调或lambda执行。

demo

参考

  1. How to match pattern except in situations s1, s2, s3
  2. 有关代码实现,请参阅How to match a pattern unless...
  3. 中的代码示例

答案 4 :(得分:0)

通过@protist调整建议的解决方案,在这种情况下,搜索短语并排除脚本标签内的所有匹配项:

(?!<script[^>]*?>)(\bTest Phrase\b)(?![^<]*?<\/script>)

Demo

亚当提供的答案虽然更为简洁,但执行起来却需要更长的时间。可以通过编辑此注释中已经提到的演示来证明这一点。