正则表达式检查重复
Posted
技术标签:
【中文标题】正则表达式检查重复【英文标题】:Regex checking repeat 【发布时间】:2012-12-12 07:24:30 【问题描述】:我正在尝试使用正则表达式检查文本行。
1,3,4,5,8,10,12,14,19,14
这里的数字用','分隔,并且应该是非负数并且小于或等于20。 而且任何数字都不应该重复。 这是我的模式。
^(?:(?:0[1-9]|[1-9]|1[0-9]|20),)*(?:0[1-9]|[1-9]|1[0-9]|20)$
但它无法检查重复。如何查看?
【问题讨论】:
正则表达式不是正确的工具。检查重复项需要内存。仅解析该行并在普通代码中检查所有这些条件会简单得多 @PaulPhillips - 实际上我认识一个这样做的人。但我不可能问他。我只是想知道这个机制,我绝对不会在现实生活中使用它。 在“真实世界”的正则表达式中它可能是可能的。在理论上它不是。 我想可能使用捕获子组和负前瞻的某种组合,但我不知道如何制定它。这肯定不是最有效的方法。 为什么你试图用正则表达式而不是真正的代码来做这个? 【参考方案1】:你想做的事情并不复杂。您只需要在每个匹配的数字之后检查该数字是否在字符串中再次出现:
^(?:(0[1-9]|[1-9]|1[0-9]|20),(?!.*\b\1\b))*(?:0[1-9]|[1-9]|1[0-9]|20)$
查看并测试它here on Regexr。
在 C# 中:
string[] myStrings = "1",
"1,2",
"01,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20",
"01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20",
"01,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,5",
"01,02,03,04,05,06,07,08,13,09,10,11,12,13,14,15,16,17,18,19,20" ;
Regex reg = new Regex(
@"^
(?:(0[1-9]|[1-9]|1[0-9]|20),
(?!.*\b\1\b) # Fail if the before matched number occurs once more
)*
(?:0[1-9]|[1-9]|1[0-9]|20)
$",
RegexOptions.IgnorePatternWhitespace
);
foreach (string myString in myStrings)
Console.WriteLine("0 1 a valid string.",
myString,
reg.IsMatch(myString) ? "is" : "is not"
);
Console.ReadLine();
【讨论】:
哇,太好了..我想知道我怎么不能早点使用这个解决方案...看起来很简单...干得好【参考方案2】:由于您已使用 C# 和 Java 标记了您的问题,因此我不会在这里为您提供代码解决方案,而是基本想法。
如果您将字符串按,
拆分,您会得到一个子字符串列表:"1", "3" , "4", "5", "8", "10", "12", "14", "19", "14"
。现在,您可以遍历这些并尝试将每个解析为整数。如果它失败了,它就不是一个数字。如果成功,您可以轻松检查它是< 0
还是> 20
。您还可以保留一组您之前已经拥有的数字,并检查当前的数字是否重复。
底线是,您不应该尝试对所有内容使用正则表达式。而且你的语言要求不是regular 无论如何(如果你需要记住东西,或者计算东西,它通常是不规则的)。基于 Perl 的正则表达式的功能不仅仅是 regular,但在这里还不够。
正则表达式解决方案
正如您在 cmets 中所说,一行最多只能容纳 20 个数字。由于每个数字也被限制在 0 到 20 之间,因此您对线条的实际外观有有限的可能性。因此,您有一种有限的语言(可能的行数有限)。有限语言是正则语言的子集,因此,您可以使用正则表达式“轻松”表示语言。
最简单的解决方案是列出所有可能的行。因此,如果每行只有 3 个数字,其中 5 是最大的数字(为了简单起见),正则表达式可能如下所示:
0,1,2|0,1,3|0,1,4|0,1,5|0,2,3|0,2,4|0,2,5|0,3,4|0,3,5|0,4,5|1,2,3|1,2,4|1,2,5|1,3,4|1,3,5|1,4,5|2,3,4
当然,你可以简化很多(甚至更多):
0,(1,(2|3|4|5)|2,(3|4|5)|3,(4|5)|4,5)|1,(2,(3|4|5)|3,(4|5)|4,5)|2,(3,(4|5)|4,5)|3,4,5
但是,是的,如果你有一个使语言有限的要求,它也会变得有规律,但不一定是漂亮的;我认为“手动”解决方案仍然更具可读性,尤其是更灵活。
【讨论】:
+1 让我在 Wikipedia 正则表达式中了解“常规”的实际含义。 @Alex +1 查找它然后;D 每种具有前瞻断言的正则表达式风格都能够轻松做到这一点,并且是最现代的编程语言,甚至是 javascript。在我看来,列出所有可能的行远非最简单的解决方案。 @stema 我知道 regexp 实现可以做得更多(我提到过),但不知道使用前瞻就这么简单。无论如何,所描述的语言不是常规的,因此我对有限版本的解释。【参考方案3】:Regex 不是最好的选择。重复数字太快了。您可能想查看标记化。即使是像寻找不存在的模式这样简单的事情也很困难(参见Regular expression to match a line that doesn't contain a word? 示例)
我会用逗号分割字符串,然后将它们添加到有序列表中。如果使用 C#:
"1,2,3,4".Split(',')
开始然后继续使用 Linq 看看你的条件是否满足。
如果您必须使用正则表达式执行此操作,请查看迭代集合搜索返回。但是,与上述解决方案相比,这给您带来的收益很少。
【讨论】:
是的,我知道负前瞻。但它不支持超过 9 个匹配项。 您是否强迫自己使用正则表达式,因为您的列表看起来足够短,无法保证将其抽象为集合? 是的,列表可以是 0 到 20 任意数字和任意数量的数字。所以在这种情况下,只有 9 个前瞻断言对我没有帮助。 与正则表达式在下面所做的相比,通过避免直接拆分字符串、解析和集合过滤,您不会获得任何收益。如果您希望代码简洁,请使用 Linq。 实际上我不会在任何代码中使用它。我只是想知道我该怎么做。因为我知道有人已经使用正则表达式做到了这一点。【参考方案4】:String[] numbers = input.split(",");
Set<Integer> filtered = new TreeSet();
for(String number: numbers)
if(!number.startsWith("-")
int nbr = Integer.parseInt(number);
if(nbr < 20)
filtered.add(nbr);
for(int nbr: filtered)
System.out.print(nbr + " ");
【讨论】:
【参考方案5】:既然你想要正则表达式,是的,你会受到反向引用的限制,因为它们只能从 \1 到 \9。所以你需要排除配对。您最大的挑战是摆脱重复的数字。
来自http://www.regular-expressions.info/refadv.html
使用 (?:(\d?\d),?)+
和 (?!<regex>)
以确保没有重复项。你也可以使用
(?(?=<regex>)true|false)
我用这个页面做实验:http://www.regextester.com/
【讨论】:
我可以看到它是一个简单的否定前瞻断言。如何解决问题? 只匹配没有其他匹配项的数字。忽略第二组并使用 \G 从第一个匹配点之后的点重新进行搜索。 看起来 stema 为您提供了完整的解决方案。我希望你能得到它?!组。以上是关于正则表达式检查重复的主要内容,如果未能解决你的问题,请参考以下文章