2

我正在尝试用 Java 编写一个方法来检查字符串并允许它只包含数字和逗号。此外,不能有重复的数字。

例如:

  • 11,22,33- 还行吧
  • 22,22,33- 这不行

我已经使用正则表达式和(如下)的组合完成了它的初稿Set<String>,但正在寻找更好的东西,最好只使用正则表达式。

public boolean isStringOk(String codes) {
    if(codes.matches("^[0-9,]+$")){ 
        Set<String> nonRepeatingCodes = new LinkedHashSet<String>();
        for(String c: codigoRoletas.split(",")){
            if(nonRepeatingCodes.contains(c)){
                return false;
            }
            else{
                nonRepeatingCodes.add(c);
            }
        }
        return true;
     }
    return false;
}

有谁知道这是否可以仅使用正则表达式?

4

4 回答 4

6

我怀疑这是可取的(正如 Jarrod Roberson 所提到的),因为对于您项目中的任何其他编码人员来说,这都很难理解。但仅使用正则表达式绝对是可能的:

^(?:(\d+)(?!.*,\1(?!\d)),)*\d+$

双重否定的前瞻性使其有点难以理解。但这里有一个解释:

^                # anchor the regex to the beginning of the string
(?:              # subpattern that matches all numbers, but the last one and all commas
    (\d+)        # capturing group \1, a full number
    (?!          # negative lookahead, that asserts that this number does not occur again
        .*       # consume as much as you want, to look through the whole string
        ,        # match a comma
        \1       # match the number we have already found
        (?!\d)   # make sure that the number has ended (so we don't get false negatives)
    )            # end of lookahead
    ,            # match the comma
)*               # end of subpattern, repeat 0 or more times
\d+              # match the last number
$                # anchor the regex to the beginning of the string

请注意,这只是一般的正则表达式,而不是特定于 Java。在 Java 中,您需要转义每个反斜杠,否则它将无法进入正则表达式引擎:

^(?:(\\d+)(?!.*,\\1(?!\\d)),)*\\d+$
于 2012-11-12T17:59:19.450 回答
2

请注意,将正则表达式用于技术上的非正则语言可能很危险,尤其是对于大的、不匹配的字符串。如果你不小心,你可以引入指数时间复杂度。此外,正则表达式引擎必须执行一些后门技巧,这些技巧也会减慢引擎的速度。

Pattern如果您尝试其他解决方案并且它们给您带来问题,您可以使用捕获组以及和类以这种方式尝试Matcher以使您的代码更清晰:

private static final Pattern PATTERN = Pattern.compile("([\\d]+),?");

public static boolean isValid(String str) {
    Matcher matcher = PATTERN.matcher(str);
    Set<Integer> found = new HashSet<Integer>();
    while (matcher.find()) {
        if (!found.add(Integer.parseInt(matcher.group(1)))
            return false;
    }
    return true;
}
于 2012-11-12T18:10:57.167 回答
1

这是我能想出的最不难看的正则表达式:

return codes.matches("^(?:,?(\\d+)(?=(?:,(?!\\1\\b)\\d+)*$))+$");

分解:

  • ,?如果有一个逗号,则使用下一个逗号(即,它不是字符串的开头)。

  • (\d+)捕获组 #1 中的下一个数字

  • (?=(?:,(?!\1\b)\d+)*$)尝试匹配剩余的数字,检查每个数字以确保它与刚刚捕获的数字不同。

\b后向引用可防止对11,111. 它在其他任何地方都不需要,但\d+如果你愿意,你可以在每一个上添加一个,它可能会使正则表达式稍微更有效率。但是,如果您需要调整正则表达式以获得最佳性能,则使所有量词都具有所有格会产生更多效果:

"^(?:,?+(\\d++)(?=(?:,(?!\\1\\b)\\d++)*+$))++$"
于 2012-11-12T19:48:28.943 回答
0

这个正则表达式会做

^(?=^\d+(?:,\d+)*$)(?!^.*?((?<=(?:^|,))\d+(?=[,$])).*?\1(?:$|,.*?)).*?$

(?=^\d+(?:,\d+)*$)检查有效格式,如 45 或 55,66,88,33

(?!^.*?((?<=(?:^|,))\d+(?=[,$])).*?\1(?:$|,.*?))如果有任何重复的数字,则不匹配..

.*?如果上述负前瞻返回 true,则匹配所有内容

这里工作

于 2012-11-12T18:23:28.907 回答