Perl & Regex:仅当字符串不在 > 和 < 之间时才替换字符串

Perl & Regex: replace string only if it is not between > and <

提问人:7beggars_nnnnm 提问时间:3/4/2021 最后编辑:7beggars_nnnnm 更新时间:3/15/2021 访问量:396

问:

我想替换 和 之间的所有字符串,例如,将 (from excerpt:) 替换为 foo,但不要替换 (from excerpt: )。><center> is the sun the center of the universe?:<center<...center;">

我正在使用以下命令:

perl -pi -w -e 's/center/foo/g;' file.html

因此,我尝试使用REGEX(PHP代码)在两个HTML标签之间替换所有“foo”,得到如下结果:

perl -pi -w -e 's/(?<![\w$<])\$\(center\)(?![\w$>])/foo/g;' file.html

但它不能正常工作。我在网上搜索了一下,最接近我需要的是 Perl 字符串替换:匹配,但不替换正则表达式的一部分,Perl 正则表达式 - 仅当字符串介于两者之间时,才在标签之间搜索和替换,并且用异常替换字符串中的文本。但是我不能完全解决仅替换非特定字符串的需要。<center>

fragment_html_code:

</td></tr><tr><th colspan="2" class="" style="text-align:center;">is the sun the center of the universe?:</th></tr><tr class=""><td colspan="2" class="" style="text-align:center;">
center </td></tr>

编辑更新:

关于Lordadmira Solution

每次在 <> 和 </> 之间出现跳线时,代码都会失败。例如,当要替换的单词是 like(这里有一个换行符)center </> 时失败。它可能会发生什么?有关上下文示例,请参阅下文:

</td></tr><tr><th colspan="2" class="" style="text-align:center;">
   (Here there is a line jump and then the solution of Lordadmira fails and does not occur) ----> is the sun the center of the universe?:
    </th></tr><tr class=""><td colspan="2" class="" style="text-align:center;">
        center </td></tr>

编辑更新01:

我将 Lordadmira 的初始解决方案修改为 or,这适用于换行符,但它删除了 foo 之后的所有内容。在删除 foo 后,我尝试了几种方法来避免文本,但一直无法得到解决方案。如果我设法解决了这个问题,那么这个问题将得到充分的回答。perl -0777 -pi -w -e 's{>\K[^<]*?\K.foo[^<]*(?=<).}{ bar }g;' file.htmlperl -0777 -pi -w -e 's{>\K[^<]*?\K.foo.[^<]*(?=<).}{ bar }g;' file.html

编辑更新02

我现在已将我在 EDIT UPDATE 01 中从 Lordadmira 的修改更改为更正之前删除了 foo 之后的文本这一事实。但这是擦除字符串的第一个字符之后,我想说的是,例如在perl -0777 -pi -w -e 's{>\K[^<]*?\K.foo.[^<](?!=<)}{ bar }g;'foo

> "lorem
  foo ipsum "< 

当被替换时,结果不符合预期,因为我得到,即 ipsum “i” 被删除foo>" lorem bar psum "<


下面的解决方案解决了每次替换删除 foo 后字符串中有一个字符的问题。就目前而言,在广泛的背景下,这是对Lordadmira最初解决方案的最有效的改编。

为了解决这个问题,有必要省略 foo 末尾的运算符点,并在不包含字符串的正则表达式匹配行添加负前瞻作为附加解释,并在“正负前瞻”部分中详尽地补贴, 将属于 Lordadmira 初始解的部分修改为 。(?=<)(?!=<)

perl -0777 -pi -w -e 's{>\K[^<]*?\K.foo[^<](?!=<)}{ bar }g;'


编辑更新 3:

经过几次测试,我相信已经为我的意图找到了最令人满意的解决方案。

perl -0777 -pi -w -e 's{>[^<]*?\K\b(foo)\b(?!=<)}{bar}g;'

html perl html 解析

评论

1赞 Jerry Jeremiah 3/4/2021
怎么样,或者你需要专门匹配“中心”吗?perl -pi -w -e 's/(?<=>)[^<]+(?=<)/foo/g;' file.html
0赞 7beggars_nnnnm 3/4/2021
@JerryJeremiah,至于指定中心,它只是为了与 MWE 一起服务,但我也需要更换许多其他字符串。尽管更好地考虑中心周围其他字符串的存在可能会影响解决方案。fragment_code_htmlcenter

答:

2赞 lordadmira 3/4/2021 #1

你会这样做的。

s{>\K[^<]*?center[^<]*(?=<)}{foo}g;

编辑:使用命令行逐行读取文件,并假定要执行的所有工作都包含在单行中。如果你需要跨行工作,你必须读入整个文件(或任何足够的块)。使用,它应该可以工作。perl -pperl -0777 -p

有关更多信息,请参见 perlrun

HTH

评论

0赞 7beggars_nnnnm 3/7/2021
每次 之间出现跳线时,代码都会失败。例如,当要替换的单词是 一样时,失败。它可能会发生什么?<> and </><tr> (here there is a line break) center </>
1赞 lordadmira 3/7/2021
当您使用开关时,它会逐行读取文件。如果匹配中可以有换行符,请一次读取整个文件。要么放在命令行上,要么制作一个简短的脚本和 .-p-0777undef $/
0赞 7beggars_nnnnm 3/7/2021
perl -0777 -p没有用,我阅读了 perlrun 中的文档并研究了网络,但没有任何解决。这里有一个示例 github.com/yaacovNaNachRabbeinu/things/blob/main/...,,在这个文件中,我想替换例如,但是当我运行Alexandrite.html时,文本没有改变。lightluzperl -0777 -pi -e 's{>k[^]*?light[^<]*?=<)}{light}g;'
0赞 7beggars_nnnnm 3/7/2021
事实上,即使文件不是 HTML 并且具有简短的文本,该命令Alexandrite_mini.txt也不起作用,因为该文件 github.com/yaacovNaNachRabbeinu/things/blob/main/...'perl -0777 -pi -e 's{>k[^]*?light[^<]*?=<)}{light}g;'
0赞 7beggars_nnnnm 3/8/2021
我已将其初始解决方案修改为 OR,这适用于换行符,但它删除了 Foo 之后的所有内容。在删除 foo 后,我尝试了几种方法来避免文本,但一直无法得到解决方案。如果我设法解决了这个问题,那么这个问题将得到充分的回答。perl -0777 -pi -w -e 's{>\K[^<]*?\K.foo[^<]*(?=<).}{ bar }g;' file.htmlperl -0777 -pi -w -e 's{>\K[^<]*?\K.foo.[^<]*(?=<).}{ bar }g;' file.html
1赞 7beggars_nnnnm 3/8/2021 #2

我的回答显然是对上面最初的@lordadmira解决方案的改编:

为了促进对Lordadmira最初解决方案的改编,有两件事是必要的:使用断行符,并在foo之后保持原始文本的完整。改编如下:

perl -0777 -pi -w -e 's{>\K[^<]*?\K.foo[^<](?!=<)}{ bar }g;'

为了解决这个问题,有必要省略 末尾的运算符点 ,并在“正负展望”一节中添加负前瞻作为附加解释,该行不包含字符串并详尽地补贴, 将属于 Lordadmira 初始解的部分修改为 。..\K.foo(?=<)(?!=<)

注意:我不确定它是否适用于代码格式或html内容的所有可能上下文,但在我迄今为止所做的测试中已经足够了。

解决方案最终(即我上面问题中的 EDIT UPDATE 3):

perl -0777 -pi -w -e 's{>[^<]*?\K\b(foo)\b(?!=<)}{bar}g;'