10

我正在尝试使用正则表达式检查文本行。

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)$

但它不能检查重复。我怎样才能检查它?

4

5 回答 5

7

你想做的事情并没有那么复杂。您只需要在每个匹配的数字之后检查该数字是否在字符串中再次出现:

^(?:(0[1-9]|[1-9]|1[0-9]|20),(?!.*\b\1\b))*(?:0[1-9]|[1-9]|1[0-9]|20)$

在 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();
于 2012-12-26T21:53:24.523 回答
4

由于您已使用 C# 和 Java 标记了您的问题,因此我不会在这里为您提供代码解决方案,而是提供基本思想。

如果你用 分割字符串,,你会得到一个子字符串列表:"1", "3" , "4", "5", "8", "10", "12", "14", "19", "14"。现在,您可以遍历这些并尝试将每个解析为整数。如果它失败了,它就不是一个数字。如果成功,您可以轻松检查它是< 0还是> 20。您还可以保留一组以前已有的数字,并检查当前的数字是否重复。

底线是,您不应该尝试对所有内容都使用正则表达式。无论如何,您的语言要求并不规律(如果您需要记住东西或计算事物,通常是不规律的)。基于 Perl 的 RegExps 的能力不仅仅是常规的,但在这里还不够。

作为正则表达式的解决方案

正如您在评论中所说,一行最多只能容纳 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

但是,是的,如果你有一个使语言有限的要求,它也会变得有规律,但不一定漂亮;我认为“手动”解决方案仍然更具可读性,尤其是更灵活。

于 2012-12-26T20:10:07.777 回答
2

正则表达式不是最好的选择。重复数字太快了。您可能想查看标记化。甚至像查找不存在的模式这样简单的事情也很困难(请参阅正则表达式以匹配不包含单词的行?例如)

我会用逗号分割字符串,然后将它们添加到有序列表中。如果使用 C#:

"1,2,3,4".Split(',')

开始然后继续使用 Linq 看看你的条件是否满足。

如果您必须使用正则表达式执行此操作,请查看迭代集合搜索返回。但这比上面的解决方案给你带来的收益很少。

于 2012-12-26T20:09:26.903 回答
1
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 + " ");
}
于 2012-12-26T20:56:16.480 回答
0

由于您需要正则表达式,是的,您将受到反向引用的限制,因为它们仅从 \1 到 \9。所以你需要排除配对。你最大的挑战是摆脱重复的数字。

来自http://www.regular-expressions.info/refadv.html

使用(?:(\d?\d),?)+with(?!<regex>)确保没有重复项。你也可以使用 (?(?=<regex>)true|false)

我用这个页面进行实验:http ://www.regextester.com/

于 2012-12-26T21:00:15.923 回答