【问题标题】:Perl regexp - replace all digits in a string with # unless they have a certain prefixPerl 正则表达式 - 用 # 替换字符串中的所有数字,除非它们有特定的前缀
【发布时间】:2014-03-30 10:30:37
【问题描述】:

我正在尝试用“#”替换字符串中的所有数字,前提是它们没有特定的前缀。 这些数字可以作为单词的一部分出现,也可以单独作为单词出现。

例如,使用 ABC 作为前缀,这是想要的结果。

输入:

sdkfjsd 12312981 sdkfjsdfhbnmawd 1298 ,smdfsdnfk2342423 
sdlkfsdfs 20349 ABC1203912 2034234aac <-- ABC<number> stays, the other numbers do not
ABC1203912

结果(注意第 2,3 行的 ABC 带有一个数字):

sdkfjsd # sdkfjsdfhbnmawd # ,smdfsdnfk#
sdlkfsdfs # ABC1203912 #aac <-- ABC<number> stays, the other numbers do not
ABC1203912

我试图用正则表达式后面的负面外观来做到这一点:s/(?&lt;!ABC)\d+/#/g。 在这种情况下,只有 ABC 之后的第一个数字不会被替换,其余的都会。

我的下一步是将字符串拆分为包含 ABC\d+ 的部分,并对其他部分执行简单的替换。

将不胜感激任何建议如何在不拆分成多个字符串的情况下完成整个事情。

谢谢!

编辑 1:将 aac 移回正确位置。 编辑 2:我正在使用 perl 5.8.5,以防万一。由于与我无法控制的代码的兼容性问题,我无法更新到较新的版本。

【问题讨论】:

  • 我不明白您所说的“我的下一步是将字符串拆分为包含ABC\d+ 的部分,并对其他部分执行简单替换。”,但您可以替换like so
  • 您的结果与您的输入不匹配,您将aac 字段向上移动。
  • @TLP - 谢谢。固定:)
  • @Jerry - 如果我找不到使用否定查看或其他任何方法的方法,我将求助于拆分字符串并处理子字符串,这将更易于解析。也欣赏这个例子,但在 Perl 中否定的外观必须是固定宽度,这样就不会运行。
  • @Oron 啊,我现在明白了。是的,TLP 早些时候提到过。 PCRE 代表 Perl Compatible RegEx,我认为它会起作用。不过这没什么大不了的,我把解决方法放在我的答案中。

标签: regex perl negative-lookbehind


【解决方案1】:

我不明白您所说的“我的下一步是将字符串拆分为包含ABC\d+ 的部分,并对其他部分执行简单替换。”,但看起来它不是您的这里的主要问题。否则请告诉我。

要匹配前面没有关键字ABC 的每个数字,那么你可以使用这个正则表达式:

(?<!ABC|\d)\d+

如果前面有 ABC 或另一个数字,这会阻止数字的匹配(因此如果从数字的中间开始,则会阻止 \d+ 匹配。

regex101 demo

请注意,您的问题中有两个部分的字符串移动了。我只接受你使用的输入。


如果上述方法不起作用(例如,正则表达式引擎说后视模式不能具有可变宽度,或类似的东西),那么替代等效项是:

(?<!ABC)(?<!\d)\d+

regex101 demo

【讨论】:

  • Variable length lookbehind not implemented in regex m/(?&lt;!ABC|\d)\d+/
  • @TLP 呵呵,我认为 perl 可以。不过这很容易解决。
  • 它适用于测试用例,但它也部分匹配,这可能是一个问题。例如。 RABC123 被保留。
  • @TLP 是的。我没有设置单词边界,因为我不知道 OP 在这种情况下是否要替换。我假设在任何情况下都应该替换以ABC 开头的数字。否则,它将带有单词边界:(?&lt;!\bABC)(?&lt;!\d)\d+
  • ABC 后面的数字不应被替换 (ABC123 ==> ABC123)。任何其他数字都应该是(甚至 123ABC ==> #ABC)。
【解决方案2】:

不完全清楚你想要什么,尤其是因为 2034234aac 字段在你的示例中被奇怪地修改了。

但是,对您自己的负面回顾的这种修改可能会很有用。请注意,它会保留以ABC 开头的完整any 序列,例如ABCX1234。目前尚不清楚这是否是正确的行为。

use strict;
use warnings;

my $s = <<'__END_TEXT__';
sdkfjsd 12312981 sdkfjsdfhbnmawd 1298 ,smdfsdnfk2342423 
sdlkfsdfs 20349 ABC1203912 2034234aac <-- ABC<number> stays, the other numbers do not
ABC1203912
__END_TEXT__

$s =~ s/\b(?!ABC)[a-z]*\K\d+/#/gi;

print $s;

或者,对于早于 10 的 Perl 5 版本,使用这个

$s =~ s/\b((?!ABC)[a-z]*)\d+/$1#/gi;

输出

sdkfjsd # sdkfjsdfhbnmawd # ,smdfsdnfk# 
sdlkfsdfs # ABC1203912 #aac <-- ABC<number> stays, the other numbers do not
ABC#

【讨论】:

  • 通过 Perl -wc 运行它会说“无法识别的转义 \K 在...处通过”。这也不会替换我实际输入中的任何数字(抱歉,很遗憾不能分享)。如果重要的话,我在这种特殊情况下使用 perl 5.8.5 并且不能更新得更高。另外 - 修复了示例,因此 aac 现在回到了正确的位置。
  • @Oron:好的,谢谢。缺乏对\K 的支持意味着您运行的是非常 旧版本的Perl。从五年前 Perl 5 的第 10 版开始,它就一直有效。我为早期的 Perls 添加了一个版本。
  • 更好,但仍然没有雪茄。例如 ww28b 没有更改为 ww#b。我在上面将杰瑞的答案标记为正确,它也适用于我的现实生活输入。不过感谢您的努力。
【解决方案3】:

您需要使用“零宽度否定后向断言”:仅在没有紧接某些内容的情况下才匹配。

例如。匹配前面没有ABC的数字:

(?<!ABC)\d

您已经做到了这一点,但下一步要匹配前缀和多个数字:

(?<!ABC)\d+

没有直接帮助,因为你需要不匹配。

所以稍微改一下问题:

替换一个不跟在前缀后面的数字和一个或多个数字

即。在“ABC123”中,您不想替换 1、2 或 3。我们可以扩展零宽度负后向断言以包含数字:

(?<!ABC\d+)\d

因此也排除了前缀后面的数字。

NB 这假设 Perl 支持可变宽度的后视:当然,正则表达式的第一个扩展包括后视,它们必须是固定宽度,但已经有一段时间了我认真地使用了 Perl 正则表达式,所以我假设 Perl 正则表达式的实现已被扩展以匹配其他平台。

编辑:糟糕,s/positive/negative/lookbehind。

【讨论】:

  • 没有这么扩展。
  • 负面的后视是(?&lt;!...)
  • @TLP 有趣:Perl 总是用来设置正则表达式的标准。
  • @Richard 还有另一种选择,转义 \K (keep) 会做类似的事情。 IE。 (?&lt;=foo) 将是 foo\K。但否定它会比较棘手。
猜你喜欢
  • 2023-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多