2014-06-21 13 views
7

Ich bin ein einfaches Debugging-Programm zu schreiben, die einfache Zeichenfolgen als Eingabe, die Sterne enthalten einen Platzhalter Match-any anzuzeigenWildcard Matching in Java

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

Ich dachte, ich würde einfach das Muster nehmen, Flucht jeder Sonderzeichen in regulären Ausdrücken, ersetzen Sie dann alle \\* zurück zu .*. Und dann verwenden Sie einen regulären Ausdruck Matcher.

Aber ich kann keine Java-Funktion finden, um einen regulären Ausdruck zu entkommen. Die beste Übereinstimmung, die ich finden konnte, ist Pattern.quote, die aber gerade \Q und \E am Anfang und Ende der Zeichenfolge setzt.

Gibt es etwas in Java, mit dem Sie einfach diesen Wildcard-Abgleich durchführen können, ohne dass Sie den Algorithmus von Grund auf neu implementieren müssen?

+1

'Pattern.quote()' und der Rest des Ansatzes sollte gut funktionieren. Was siehst du, was anderes vermuten lässt? –

+0

@MattBall Ich werde mit '\ Q (. *, A) \ E' enden, die nicht mit' (foo, a) 'übereinstimmen, weil' foo' nicht mit dem Literal '. *' Übereinstimmt. –

+0

Ich glaube, ich verstehe falsch, was das Endziel ist. Sie erhalten eine Zeichenfolge wie '* .wav'. Was wird damit passieren? –

Antwort

8

Eine einfache regex

Einer dieser Vorteile des Verfahrens ist, dass wir leicht Token neben * hinzufügen können (siehe Tokens Hinzufügen an der Unterseite).

Suche: [^*]+|(\*)

  • Die linke Seite des | passt alle Zeichen, die kein Stern
  • Die rechte Seite alle Sterne zu Gruppe erfasst sind 1
  • Wenn Gruppe 1 ist leer: Ersetzen Sie mit \Q + Match + E
  • Wenn Gruppe 1 gesetzt ist: ersetzen mit .*

Hier ist ein Arbeitscode (siehe die Ausgabe des online demo).

Eingang: audio*2012*.wav

Ausgang: \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); 

Tokens Hinzufügen

Angenommen, wir wollen auch das Wildcard-? konvertieren, die für ein einzelnes Zeichen steht, die durch einen Punkt.Wir fügen Sie einfach eine Capture-Gruppe an die Regex, und aus dem matchall auf der linken Seite ausschließen:

Suche: [^*?]+|(\*)|(\?)

In der Ersetzen-Funktion die wir so etwas wie hinzufügen:

else if(m.group(2) != null) m.appendReplacement(b, "."); 
+0

sieht das bisher am besten aus. darauf warten, dass jemand vielleicht noch eine einfachere Lösung findet. Vielen Dank! –

+0

Was ich mag ist, dass wenn Sie das Einzelzeichen '?' Token hinzufügen wollen, das in der Wildcard-Anpassung verwendet wird, es ein Kinderspiel ist: '[^ *?] + | (\ *) | (\?)', Dann in der Replace-Funktion fügen wir 'if (m.gruppe (2)! = null) hinzu m.appendReplacement (b,". ");' (da der Punkt ein einzelnes Zeichen ist) – zx81

+0

Nicht 'wildcardSpec.replaceAll (" [^ *] + "," \\\\ Q $ 0 \\\\ E "). ReplaceAll (" \\ * + ",". * ")' Funktionieren auch? –

13

Nur entkommen Sie alles - kein Schaden wird daraus entstehen.

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 

Oder können Sie Zeichenklassen verwenden:

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

Es ist einfacher, die Zeichen „zu entkommen“, indem sie in einer Zeichenklasse setzen, wie fast alle Charaktere verlieren eine besondere Bedeutung, wenn sie in einer Zeichenklasse . Sofern Sie keine merkwürdigen Dateinamen erwarten, wird dies funktionieren.

+0

Hmm, warum habe ich nicht daran gedacht. Es scheint zu einfach. Vielen Dank! –

+1

Hmm, das scheint leider nicht zu funktionieren. Java beschwert sich "Illegal/nicht unterstützte Escape-Sequenz in der Nähe von Index 3 \ f \ o \ o". Scheinbar erlaubt es nur, eine begrenzte Anzahl von Zeichen zu umgehen: "Es ist ein Fehler, vor einem Buchstaben ein Backslash zu verwenden, der kein maskiertes Konstrukt kennzeichnet; diese sind für zukünftige Erweiterungen der regulären Ausdrucksprache reserviert." –

+0

Haben Sie Paste kopiert? Dieser Code wird ohne Fehler ausgeführt. Ich kann nur davon ausgehen, dass Sie 'replaceAll()' anstelle von 'replace()' für den zweiten Methodenaufruf codiert haben. Ist das passiert? – Bohemian

1

Sie können Verwenden Sie auch die Notice Escape-Zeichen: \\Q and \\E - alles zwischen ihnen wird als Literal behandelt und nicht als Teil der auszuwertenden Regex betrachtet. So soll dieser Code funktionieren: (?)

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

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

Beachten Sie, dass Ihre Platzhalter * vielleicht auch am besten nur gegen Wortzeichen w je nach Verwendung \ angepasst werden, wie Sie Ihren Wildcard verhalten sollen

0

Lucene Klassen hat, dass bieten Sie diese Möglichkeit mit zusätzlicher Unterstützung für Backslash als Escape-Zeichen. ? entspricht einem einzelnen Zeichen, 1 entspricht 0 oder mehr Zeichen, \ entkommt das folgende Zeichen. Unterstützt Unicode-Codepunkte. Angeblich schnell, aber ich habe es nicht getestet.

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

Regex Während Accommodating A DOS/Windows-Pfad

Umsetzung der Quotation Escape-Zeichen \Q und \E ist wahrscheinlich der beste Ansatz. Da ein Backslash jedoch normalerweise als DOS/Windows-Dateitrennzeichen verwendet wird, könnte eine "\E" -Sequenz innerhalb des Pfads die Paarung \Q und \E bewirken. Während Abrechnung der * und ? Wildcard-Token, könnte diese Situation des Backslash auf diese Weise behandelt werden:

Suche: [^*?\\]+|(\*)|(\?)|(\\)

Zwei neue Linien in der Ersetzen-Funktion des „Mit einem einfachen Regex hinzugefügt werden würden "Beispiel für das neue Suchmuster. Der Code wäre immer noch "Linux-freundlich". Als ein Verfahren, könnte es wie folgt geschrieben werden:

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(); 
} 

-Code die Implementierung dieser Methode könnte demonstrieren wie folgt geschrieben werden:

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

Dies würde die generierten Ergebnisse sein:

Input: C:\Temp\Extra\audio??2012*.wav 
Output: \QC:\E\\\QTemp\E\\\QExtra\E\\\Qaudio\E..\Q2012\E.*\Q.wav\E