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 に答える