5

2つの主要なワイルドカードを許可し、データをフィルタリングしたいと?思い*ます。

これが私が今やっている方法です(私が多くのウェブサイトで見たように):

public boolean contains(String data, String filter) {
    if(data == null || data.isEmpty()) {
        return false;
    }
    String regex = filter.replace(".", "[.]")
                         .replace("?", ".")
                         .replace("*", ".*");
    return Pattern.matches(regex, data);
}

|しかし、 orなどの他のすべての正規表現の特殊文字をエスケープするべきではありません(か?そしてまた、多分私達は保存することができ?*それらの前に\?たとえば、次のようになります。

filter.replaceAll("([$|\\[\\]{}(),.+^-])", "\\\\$1") // 1. escape regex special chars, but ?, * and \
      .replaceAll("([^\\\\]|^)\\?", "$1.")           // 2. replace any ? that isn't preceded by a \ by .
      .replaceAll("([^\\\\]|^)\\*", "$1.*")          // 3. replace any * that isn't preceded by a \ by .*
      .replaceAll("\\\\([^?*]|$)", "\\\\\\\\$1");    // 4. replace any \ that isn't followed by a ? or a * (possibly due to step 2 and 3) by \\

あなたはそれについてどう思いますか?同意する場合、他の正規表現の特別な文字がありませんか?


編集#1(dan1111とm.buettnerのアドバイスを考慮した後):

// replace any even number of backslashes by a *
regex = regex.replaceAll("(?<!\\\\)(\\\\\\\\)+(?!\\\\)", "*");
// reduce redundant wildcards that aren't preceded by a \
regex = regex.replaceAll("(?<!\\\\)[?]*[*][*?]+", "*");
// escape regexps special chars, but \, ? and *
regex = regex.replaceAll("([|\\[\\]{}(),.^$+-])", "\\\\$1");
// replace ? that aren't preceded by a \ by .
regex = regex.replaceAll("(?<!\\\\)[?]", ".");
// replace * that aren't preceded by a \ by .*
regex = regex.replaceAll("(?<!\\\\)[*]", ".*");

これはどうですか?


編集#2(dan1111のアドバイスを考慮した後):

// replace any even number of backslashes by a *
regex = regex.replaceAll("(?<!\\\\)(\\\\\\\\)+(?!\\\\)", "*");
// reduce redundant wildcards that aren't preceded by a \
regex = regex.replaceAll("(?<!\\\\)[?]*[*][*?]+", "*");
// escape regexps special chars (if not already escaped by user), but \, ? and *
regex = regex.replaceAll("(?<!\\\\)([|\\[\\]{}(),.^$+-])", "\\\\$1");
// replace ? that aren't preceded by a \ by .
regex = regex.replaceAll("(?<!\\\\)[?]", ".");
// replace * that aren't preceded by a \ by .*
regex = regex.replaceAll("(?<!\\\\)[*]", ".*");

目標は見えますか?

4

3 に答える 3

2

1 つのバックスラッシュを書き出すために、置換文字列に 4 つのバックスラッシュは必要ありません。2 つのバックスラッシュで十分です。

また、否定後読みを使用して、置換文字列内の([^\\\\]|^)とを回避できます。$1

filter.replaceAll("([$|\\[\\]{}(),.+^-])", "\\$1") // 1. escape regex special chars, but ?, * and \
      .replaceAll("(?<!\\\\)[?]", ".")           // 2. replace any ? that isn't preceded by a \ by .
      .replaceAll("(?<!\\\\)[*]", ".*")          // 3. replace any * that isn't preceded by a \ by .*

最後のステップが何のために必要なのか、私にはよくわかりません。メタ文字をエスケープするバックスラッシュをエスケープしないでください (実際にはエスケープしていません)。あなたの置換呼び出しが 2 つだけではなく 4 つのバックスラッシュを書き出すという事実を無視しています。しかし、元の入力がth|is. 次に、最初の交換はそれを行いますth\|is。次に、最後の置換によりth\\|isth-バックスラッシュまたは is.

文字列がコードでどのように記述されるか (コンパイルされていない、バックスラッシュが 2 倍になる) と、コンパイルされた後の外観 (バックスラッシュが半分だけ含まれる) を区別する必要があります。

可能な の数を制限することも検討して*ください。.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*!(!入力で見つからない) のような正規表現は、実行にかなりの時間がかかる場合があります。この問題は壊滅的なバックトラッキングと呼ばれます。

于 2012-12-13T15:25:00.433 に答える
0

最後に、私が採用したソリューションを次に示します ( Apache Commons Langライブラリを使用)。

public static boolean isFiltered(String data, String filter) {
    // no filter: return true
    if (StringUtils.isBlank(filter)) {
        return true;
    }
    // a filter but no data: return false
    else if (StringUtils.isBlank(data)) {
        return false;
    }
    // a filter and a data:
    else {
        // case insensitive
        data = data.toLowerCase();
        filter = filter.toLowerCase();
        // .matches() auto-anchors, so add [*] (i.e. "containing")
        String regex = "*" + filter + "*";
        // replace any pair of backslashes by [*]
        regex = regex.replaceAll("(?<!\\\\)(\\\\\\\\)+(?!\\\\)", "*");
        // minimize unescaped redundant wildcards
        regex = regex.replaceAll("(?<!\\\\)[?]*[*][*?]+", "*");
        // escape unescaped regexps special chars, but [\], [?] and [*]
        regex = regex.replaceAll("(?<!\\\\)([|\\[\\]{}(),.^$+-])", "\\\\$1");
        // replace unescaped [?] by [.]
        regex = regex.replaceAll("(?<!\\\\)[?]", ".");
        // replace unescaped [*] by [.*]
        regex = regex.replaceAll("(?<!\\\\)[*]", ".*");
        // return whether data matches regex or not
        return data.matches(regex);
    }
}

@dan1111 と @m.buettner の貴重な助けに感謝します。;)

于 2012-12-14T14:57:07.790 に答える
0

この単純なバージョンを試してください:

String regex = Pattern.quote(filter).replace("*", "\\E.*\\Q").replace("?", "\\E.\\Q");

これはフィルタ全体を\Qandで引用し、次にand\Eでの引用を停止し、それらを対応するパターン ( and )に置き換えます。*?.*.

でテストしました

String simplePattern = "ab*g\\Ei\\.lmn?p";
String data = "abcdefg\\Ei\\.lmnop";
String quotedPattern = Pattern.quote(simplePattern);
System.out.println(quotedPattern);
String regex = quotedPattern.replace("*", "\\E.*\\Q").replace("?", "\\E.\\Q");
System.out.println(regex);
System.out.println(data.matches(regex));

出力:

\Qab*g\E\\E\Qi\.lmn?p\E
\Qab\E.*\Qg\E\\E\Qi\.lmn\E.\Qp\E
true

これは Oracle の の実装に基づいていることに注意してPattern.quoteください。他に有効な実装があるかどうかはわかりません。

于 2013-03-01T15:02:33.863 に答える