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

qybjjes1  于 2023-03-31  发布在  其他
关注(0)|答案(6)|浏览(93)

我有以下字符串:

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.标签内的文本“测试”也被替换(例如<a href="http://Test.com/url">
1.如果标签之间的文本与搜索到的文本不完全匹配,则也会被替换(例如<a href="http://url">Test xyz</a>
我希望有人有办法解决这个问题。

i5desfxk

i5desfxk1#

回答我

使用

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

说明

让我提醒你一些符号的含义:
1)?!是一个negative lookahead,例如r(?!d)选择所有不直接跟有dr

2)因此,永远不要在没有字符的情况下启动负向前看。只有(?!d)是没有意义的:

3)?可以用作惰性匹配。
123EEE
整个字符串123EEE。然而,.+?E根据需要选择尽可能少的“任何字符”(.+)。它只会选择123E

回答:

原生生物的答案是你应该使用(?!<a[^>]*?>)(Test)(?![^<]*?</a>)。让我先解释一下如何使这个更短。
正如2)中提到的,在匹配之前放置一个lookahead是没有意义的。所以下面的答案相当于原生生物的答案:

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

另外,由于<是不允许的,因此惰性匹配?是多余的,因此它也等效于

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

这将选择所有后面没有</a>且中间没有符号<Test。这就是为什么出现在任何<a ...> .. </a>之前或之后的Test将被替换。
但是,请注意

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中是非法的(smth像<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
sq1bmfud

sq1bmfud2#

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

与zb226相同,但使用延迟匹配进行了优化
另外,不建议在原始HTML上使用正则表达式。

xnifntxz

xnifntxz3#

这应该可以达到目的:

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

自己试试on regexr.

**Follow-up:**正如Adam解释above,第一部分没有效果,可以完全删除:

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

o2g1uqev4#

重新提出这个古老的问题,因为它有一个简单的解决方案,没有提到。
有了所有关于使用正则表达式解析html的声明,这里有一个简单的方法。

Perl / PCRE方法

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

demo

通用解决方案

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

在这个版本中,要替换的文本在Group 1中捕获,替换由简单的回调或lambda执行。
demo

参考

  1. How to match pattern except in situations s1, s2, s3
    1.有关代码实现,请参见How to match a pattern unless...中的代码示例。
5uzkadbs

5uzkadbs5#

调整@protist提出的解决方案,在这种情况下搜索短语并排除脚本标签内的任何匹配:

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

Demo
Adam提供的答案虽然更简洁,但执行起来需要更长的时间。这可能通过编辑本评论中已经提到的演示来证明。

soat7uwm

soat7uwm6#

in_short

对于嵌套<a>情况:
(?<tagWrap><a>(?<m>(\g<tagWrap>)|.)*?<\/a>)(*SKIP)(*FAIL)|(Test)

详情

用于排除html <a>(nest)


〈〈not_good-in_nest_case


〈〈在嵌套情况下工作

  • 正则表达式
  • ((.)(?!(.(?!<a))*<\/a>))(not_good_in_nest_case)
  • (?!<a[^>]*?>)(.)(?![^<]*?<\/a>)(not_good_in_nest_case)
  • (?<!<a>(.(?!<\/a>))*?).(not_good_in_nest_case)
  • <a[^>]*>[^<]*<\/a(*SKIP)(*F)|.(not_good_in_nest_case)
    *一米七三

〈- <a>(?<m>(?R)|(?:.(?!<a>|<\/a>))*.)*?<\/a>working)(PCRE)

  • 标签:gms
  • 示例文本(嵌套<a>时的情况)
this Test this
<a>this Test this
<a>this Test this</a>
this Test this</a>

this Test this
<a>this Test this
<a>this Test this</a>
this Test this</a>

this Test this
<a>this Test this
<a>this Test <a>this <em>Test</em> this</a>this</a> more <a>this Test this</a>
this Test this</a>this Test this

1.解释:

  • x1米11米1x

--匹配所有字符,除了(/skip)(?<tagWrap><a>(?<m>(\g<tagWrap>)|.)*?<\/a>)中的字符

  • (?<tagWrap><a>(?<m>(\g<tagWrap>)|.)*?<\/a>)

--匹配所有<a>XXXXX</a>,包括嵌套的<a>XXXXX</a>

  1. (?<m>(\g<tagWrap>)|.)*?
    --匹配<a>XXXXX</a>中的XXXXX(\g<tagWrap>)尝试尽可能进行递归
    --即:
    (\g<tagWrap>)尽可能递归匹配<a>;
    如果成功,则进入另一个递归;
    如果失败,|.匹配该字符--它不是<a>;
    *?确保:
    1.对XXXXX中的每个字符尝试/检查递归(\g<tagWrap>);
  2. |.**匹配XXXXX**中的所有字符(-当不需要递归时);
    <a>XXXXX</a>XXXXX,但不包括开始标记<a>/</a>--
    <a>将与递归BB的(下一个)开始匹配,
    </a>将与递归BB的(下一个)结束匹配);
  • 次要:
  • 使用(.(?!<a>))*匹配所有内容,直到(/break at)<a>,这是一个很好的提示。
  • 次要:
  • 递归简单表达式
  • \((a|(?R))\)

((a))/(((a)))/((((a))))(匹配这些)

用于排除html注解<!-- -->


〈〈工作(第三个)

  • 正则表达式
  • .(?!(.(?!<!--))*-->)(not_good)
  • (?<!<!--.*?)(.)|(.)(?!.*?-->)(not_working)
    *(?<!<!--(.(?!-->))*?).工作)(Javascript正则表达式实现)
  • 标记:gms
  • 示例文本(注解嵌套且格式不正确时包括大小写)
this Test this
<!--this Test this
<!--this Test this-->
this Test this-->

this Test this
<!--this Test this
<!--this Test this-->
this Test this-->
  • 解释:
  • (?<!<!--(.(?!-->))*?).

--匹配html注解外的每个字符(但<!---->仍然匹配...)

  • <!--(.(?!-->))*? match _ 1st / 1st + 2nd / 1st + 2nd + 3rd /..._ characters从<!--开始,一直到.(?!-->)--就在最近的-->之前。

相关问题