正则表达式:如果原始字符串与某些过滤器匹配,如何替换另一个字符串中所有出现的字符串

regex: how to replace all occurrences of a string within another string, if the original string matches some filter

我曾尝试使用正向预测来不消耗我想要替换字符的字符串,但不知何故我无法按预期进行替换。

这是我到目前为止所做的尝试以及结果: (请注意过滤器 - 这里 [0-9]+ 只是一个示例,将从调用站点传入,我不能直接影响它。

预期结果: 9999997890

perl -e '$x = "4564567890"; $x =~ s/(?=^[0-9]+$)456/999/g; print $x'

实际结果: 9994567890

  1. 这仅替换第一次出现的 456。为什么会这样?
  2. 我更难理解的是,如果我将过滤器前瞻更改为 (?=.*),则 456 的两次出现都将被替换。为什么更改过滤器会对正则表达式的替换部分产生任何影响?

我似乎遗漏了一些关于如何在一个 s 命令中混合过滤和替换内容的非常基本的要点。

您的正则表达式仅替换仅由数字组成的字符串开头的 456

您可以使用

s/(?:\G(?!^)|^(?=\d+$))\d*?\K456/999/g

regex demo

图案详情

  • (?:\G(?!^)|^(?=\d+$)) - 匹配上一个成功匹配的结尾 (\G(?!^)) 或 (|) 字符串开头 (^) 的自定义边界仅包含数字 ((?=\d+$))
  • \d*? - 0+ 位,但越少越好
  • \K - 省略当前匹配的字符
  • 456 - 456 子串。

想法是:

  • 使用基于 \G 的模式预验证字符串:(?:\G(?!^)|^(?=<YOUR_VALID_LINE_FORMAT>$))
  • 然后在上述之后调整消费模式。

或者,您可以使用 (*SKIP)(*F) 来跳过不仅仅由数字组成的字符串。

s/^\d*\D.*(*SKIP)(*F)|456/999/g

See this demo at regex101 or your demo at tio.run

左侧部分 ^\d*\D.* 尝试匹配任何 \D 非数字 。如果找到,则跳过 .* 字符串的其余部分并失败 | 或匹配指定的子字符串 456.