在同一文本中进行多次查找与替换
我需要在一个文件里把一些文本块重新排序。就这个任务而言,我部分成功了,也就是说现在只能把第一个文本块成功移动。
文件中的文本看起来是这样的:
text regtext1 text regtext2 text regtextA regtextZ end
其中 text 是某些随机文本,regtext1,2,3 是符合某些正则规则/模式的文本片段。它们几乎都可以包含任意可打印字符,甚至还有一些额外的字符(变音符、行尾等)。
我现在做的事情大致是这样的:
/(reg)(text\d+.*?)(regtext[A-Z]+)/$1$3$2/gs
结果是 regextA 被移动到了 regtext1 里面:
text regregtextAtext1 text regtext2 text regtextZ end
问题在于替换完成后,搜索与替换会在位于 regtextA 之后、regtextZ之前的位置继续进行——如果我对算法理解正确的话。
我该如何以某种方式修改搜索与替换的表达式,使得对 regtext2...regtextZ,以及所有其他类似情况也能做同样的处理?最终文本应该是:
text regregtextAtext1 text regregtextZtext2 text end
但这并没有发生。
我可能需要使用 \G锚点,但我不知道该怎么用。为了调试,我使用 regex101.com。
$s =~ s{(?:\G(?!\A)|)\K(reg)(text\d+.*?)(regtext[A-Z]+)}{"$1$3$2"}
但它也只产生了一次替换——大概是因为我不完全理解原始代码(以及 \G)的工作原理。
我尝试了答案中给出的正确版本的代码,但它耗费了“无穷大”时间(实际情况是在几分钟后我强制停止了执行)——就像前面的例子一样——即使我把执行限制为只进行一次替换。while的存在似乎是“有害”的。如果没有while,一次替换就会“立刻”发生。
解决方案
感谢大家的努力——答案其实挺不错的。
不幸的是,前面的回答都没有真正解决我的问题(就像我最初的尝试一样)。具体表现是:即使是第一次替换,执行时间也太长——在几分钟以上。把这个放到7500多次替换上,简直不可接受。
不过,我确实解决了这个问题,想把我做的事情分享给大家——可能对其他人有帮助。我会尽量简短。
- 我把 regtextA regtextZ 从文件末尾移动到了开头。我没有一个合理的解释,但没有其他办法能奏效。因此,我对原始的搜索与替换正则进行了调整。这个正则一次完成一个替换,但速度很快,所以我把它放进了while() 循环——这次没有再造成问题。我不明白为什么,但我很高兴它能工作。
- 我注意到对于几百次替换,速度很快(大约每秒10次替换,挺好),后来就变得极慢(每次替换十几分钟)。
- 我发现源文件有一些内容问题,导致在某些情况下无法匹配。经过一些努力,我把所有这些问题都修正了。事实上,我重复这一步好几次,直到从这个问题的角度来看,一切“完美无缺”。
- 一旦内容“完美”,一切都变得顺利,整个工作在几分钟内就完成了。
结论:不仅要有好的正则表达式,还要有好的输入数据!
开放性问题:为什么当某些输入数据不合适导致没有找到匹配时,正则引擎没有直接很快结束并跳到下一个项?答案很可能在正则引擎实现的错综复杂的内部机制里。对于这次任务而言,这个问题现在不再重要(我希望)。