web-dev-qa-db-ja.com

Javaでのワイルドカード一致

ワイルドカード一致を示すために星を含むことができる単純な文字列を入力として受け取る単純なデバッグプログラムを書いています

*.wav  // matches <anything>.wav
(*, a) // matches (<anything>, a)

私は単純にそのパターンを取り、その中のすべての正規表現の特殊文字をエスケープしてから、\\* 戻る .*。そして、正規表現マッチャーを使用します。

しかし、Java正規表現をエスケープする関数は見つかりません。見つけることができる最良の一致はPattern.quote、ただし\Qおよび\E文字列の始めと終わり。

Javaには、アルゴリズムを最初から実装しなくても、ワイルドカード照合を簡単に実行できるものがありますか?

単純な正規表現の使用

このメソッドの利点の1つは、_*_のほかにトークンを簡単に追加できることです(下部のトークンの追加を参照)。

検索:[^*]+|(\*)

  • _|_の左側は、スターでない文字に一致します
  • 右側はすべての星をグループ1にキャプチャします
  • グループ1が空の場合:_\Q_ +一致+ Eに置き換えます
  • グループ1が設定されている場合:_.*_で置き換えます

ここにいくつかの作業コードがあります( online demo の出力を参照してください)。

入力:_audio*2012*.wav_

出力:_\Qaudio\E.*\Q2012\E.*\Q.wav\E_

_String subject = "audio*2012*.wav";
Pattern regex = Pattern.compile("[^*]+|(\\*)");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, ".*");
    else m.appendReplacement(b, "\\\\Q" + m.group(0) + "\\\\E");
}
m.appendTail(b);
String replaced = b.toString();
System.out.println(replaced);
_

トークンの追加

単一の文字を表すワイルドカード_?_もドットで変換したいとします。キャプチャグループを正規表現に追加し、左側のマッチオールから除外します。

検索:[^*?]+|(\*)|(\?)

Replace関数に、次のようなものを追加します。

_else if(m.group(2) != null) m.appendReplacement(b, "."); 
_
13
zx81

すべてをエスケープしてください-害はありません。

    String input = "*.wav";
    String regex = ("\\Q" + input + "\\E").replace("*", "\\E.*\\Q");
    System.out.println(regex); // \Q\E.*\Q.wav\E
    System.out.println("abcd.wav".matches(regex)); // true

または、文字クラスを使用できます。

    String input = "*.wav";
    String regex = input.replaceAll(".", "[$0]").replace("[*]", ".*");
    System.out.println(regex); // .*[.][w][a][v]
    System.out.println("abcd.wav".matches(regex)); // true

ほぼすべての文字が文字クラスにある場合、特別な意味を失うため、文字を文字クラスに配置することで文字を「エスケープ」する方が簡単です。奇妙なファイル名を期待しているのでない限り、これは機能します。

15
Bohemian

Apache Commons-IOライブラリには、org.Apache.commons.io.FilenameUtils#wildcardMatch()という小さなユーティリティメソッドがあり、正規表現を複雑にすることなく使用できます。

APIドキュメントは次の場所にあります: https://commons.Apache.org/proper/commons-io/javadocs/api-2.5/org/Apache/commons/io/FilenameUtils.html#wildcardMatch(Java.lang .String、%20Java.lang.String)

8
Marek Gregor

引用エスケープ文字を使用することもできます:\\Q and \\E-それらの間のすべてがリテラルとして扱われ、評価される正規表現の一部とは見なされません。したがって、このコードは機能するはずです。

    String input = "*.wav";
    String regex = "\\Q" + input.replace("*", "\\E.*?\\Q") + "\\E";

    // regex = "\\Q\\E.*?\\Q.wav\\E"

*ワイルドカードは、ワイルドカードの動作(?)

1
Matt Coubrough

DOS/Windowsパスに対応するときの正規表現

引用エスケープ文字_\Q_および_\E_を実装するのがおそらく最善の方法です。ただし、円記号は通常、DOS/Windowsファイルの区切り文字として使用されるため、パス内の「_\E_」シーケンスは、_\Q_と_\E_のペアに影響を与える可能性があります。 _*_および_?_ワイルドカードトークンを考慮に入れている間、バックスラッシュのこの状況は次のようにして対処できます。

検索:[^*?\\]+|(\*)|(\?)|(\\)

新しい検索パターンに対応するために、「Using a Simple Regex」の例のreplace関数に2つの新しい行が追加されます。コードはまだ「Linuxフレンドリー」です。メソッドとしては、次のように書くことができます。

_public String wildcardToRegex(String wildcardStr) {
    Pattern regex=Pattern.compile("[^*?\\\\]+|(\\*)|(\\?)|(\\\\)");
    Matcher m=regex.matcher(wildcardStr);
    StringBuffer sb=new StringBuffer();
    while (m.find()) {
        if(m.group(1) != null) m.appendReplacement(sb, ".*");
        else if(m.group(2) != null) m.appendReplacement(sb, ".");     
        else if(m.group(3) != null) m.appendReplacement(sb, "\\\\\\\\");
        else m.appendReplacement(sb, "\\\\Q" + m.group(0) + "\\\\E");
    }
    m.appendTail(sb);
    return sb.toString();
}
_

このメソッドの実装を示すコードは、次のように書くことができます。

_String s = "C:\\Temp\\Extra\\audio??2012*.wav";
System.out.println("Input: "+s);
System.out.println("Output: "+wildcardToRegex(s));
_

これは生成された結果です:

_Input: C:\Temp\Extra\audio??2012*.wav
Output: \QC:\E\\\QTemp\E\\\QExtra\E\\\Qaudio\E..\Q2012\E.*\Q.wav\E
_
0
J. Hanney

Luceneには、この機能を提供するクラスがあり、エスケープ文字としてのバックスラッシュの追加サポートがあります。 ?は単一の文字に一致し、1は0文字以上に一致し、\は次の文字をエスケープします。 Unicodeコードポイントをサポートします。高速だと思われますが、私はテストしていません。

CharacterRunAutomaton characterRunAutomaton;
boolean matches;
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Walmart")));
matches = characterRunAutomaton.run("Walmart"); // true
matches = characterRunAutomaton.run("Wal*mart"); // false
matches = characterRunAutomaton.run("Wal\\*mart"); // false
matches = characterRunAutomaton.run("Waldomart"); // false
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Wal*mart")));
matches = characterRunAutomaton.run("Walmart"); // true
matches = characterRunAutomaton.run("Wal*mart"); // true
matches = characterRunAutomaton.run("Wal\\*mart"); // true
matches = characterRunAutomaton.run("Waldomart"); // true
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Wal\\*mart")));
matches = characterRunAutomaton.run("Walmart"); // false
matches = characterRunAutomaton.run("Wal*mart"); // true
matches = characterRunAutomaton.run("Wal\\*mart"); // false
matches = characterRunAutomaton.run("Waldomart"); // false
0
Paul Jackson