什么正则表达式可以从字符串中删除重复项?

10
给定一个由:分隔的标识符字符串,是否可能构造一个正则表达式来提取唯一的标识符到另一个字符串中,也由:分隔?
如何使用正则表达式实现这一点?我尝试了s /(:[^:])(.*)\1/$1$2/g,但没有成功,因为(.*)是贪婪的,并跳过了$1的最后匹配。
例如:a:b:c:d:c:c:x:c:c:e:e:f应该给出a:b:c:d:x:e:f 注意:我正在使用perl编程,但非常希望使用正则表达式。

1
请问您能否提供一个示例来说明您在寻找什么,我还不是很明白。 - Anders
5个回答

11
在.NET中支持lookbehind内部的无限重复,您可以搜索
(?<=\b\1:.*)\b(\w+):?

将所有匹配项替换为空字符串。

Perl(至少是Perl 5)仅支持固定长度的“向前查找”,因此您可以尝试以下方法(使用“向前查找”,结果略有不同):

\b(\w+):(?=.*\b\1:?)

如果您将其替换为空字符串,则所有重复条目的之前的重复将被删除;最后一个将保留。因此,不要使用


a:b:c:d:x:e:f

你将获得

a:b:d:x:c:e:f

如果没问题,您可以使用。
$subject =~ s/\b(\w+):(?=.*\b\1:?)//g;

解释:

第一个正则表达式:

(?<=\b\1:.*):检查是否可以匹配回溯引用编号为1的内容,后跟在字符串中之前出现的冒号。

\b(\w+):?:匹配标识符(从单词边界到下一个:),可选地后跟冒号。

第二个正则表达式:

\b(\w+)::匹配标识符和冒号。

(?=.*\b\1:?):然后检查是否可以在字符串中某处匹配相同的标识符,可选地后跟冒号。


输出顺序对我来说不重要,这就是为什么我在问题中没有提到它的原因(也许我应该提到它是无关紧要的:)。谢谢,它像魔法一样奏效了! - Tom
请更新你的答案,你提供的解决方案只适用于单个字符长的单词。也忘了提到这一点。一个更好的答案是s/\b(\w+):(?=.*\1:?)//g - Tom
@Tom:非常好的观点。我已经更新了我的答案。在反向引用前面还需要单词边界断言。 - Tim Pietzcker
你测试过那个.NET正则表达式吗?在我加上RightToLeft修饰符之前,它对我不起作用。 - Alan Moore
@Alan Moore:我在RegexBuddy中测试了它;我不认为它建议使用这个修饰符 - 感谢您的信息! - Tim Pietzcker
非常棒的答案 - 我自己也得出了类似的 (?U)(.*,)(?=(.*,|)\1) 用于逗号分隔的字符串(我的字符串的最后一个字符也是逗号),所以你的答案证实了我找到了正确的解决方案。之前没有找到你的答案,是为了避免自己去琢磨,但我不后悔,因为艰难的方式才是学习这种东西的唯一方式 ;) - Yin Cognyto

2

2
...但对于这个问题并不适用,因为这些解决方案只处理相邻的重复项... - Tim Pietzcker

1
$str = q!a:b:c:d:c:c:x:c:c:e:e:f!;

1 while($str =~ s/(:[^:]+)(.*?)\1/$1$2/g);

say $str

输出:

a:b:c:d:x:e:f

空的 while 循环加 1,但我认为更完整的解决方案可能是:while {$str =~ s/(:[^:]+|[^:]+:)(.*)\1(.*)/$1$2$3/g} 来检查第一个字母。 - NorthGuard

0

这里有一个awk版本,不需要正则表达式。

$ echo "a:b:c:d:c:c:x:c:c:e:e:f" | awk -F":" '{for(i=1;i<=NF;i++)if($i in a){continue}else{a[$i];printf $i}}'
abcdxef

将字段按“:”分割,遍历分割后的字段,将元素存储在数组中。检查是否存在,如果存在,则跳过。否则打印出来。你可以很容易地将这段代码翻译成Perl。


0
如果标识符已经排序,你可以使用前瞻/后顾来实现。如果没有排序,则超出了正则表达式的计算能力。现在,仅仅因为使用正则表达式无法实现并不意味着如果你使用一些 Perl 特定的正则表达式功能就不可能实现,但是如果你想保持你的正则表达式可移植性,你需要用支持变量的语言来描述这个字符串。

排序不相关,看看我的解决方案。 - Tim Pietzcker
什么是Perl特定功能?捕获组,反向引用,单词边界和前瞻在很多地方得到广泛支持。在本讨论中使用的功能中,我认为唯一不可移植的是后顾,尤其是无限制的后顾。 - Alan Moore
@Tim:我认为这是相关的,如果标识符被排序,消除重复将是微不足道的:s/(\w+)(:\1)+(?=:|$)/$1/g - Alan Moore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接