2014-06-21 21 views
7

Piszę prosty program debugowania, do którego wprowadzany prostych ciągów, które mogą zawierać gwiazdki aby wskazać wieloznaczny mecz każdyWildcard dopasowanie w Javie

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

myślałem, że po prostu przyjąć, że wzór, ucieczka dowolny Wyrażenia regularne w nim, a następnie zastąp każdy \\* powrotem do .*. A następnie użyj wyrażeń regularnych.

Ale nie mogę znaleźć żadnej funkcji Java, aby uciec od wyrażenia regularnego. Najlepszym dopasowaniem, jakie mogłem znaleźć, jest Pattern.quote, które jednak po prostu umieszcza \Q i \E na początku i końcu łańcucha.

Czy jest coś w języku Java, które pozwala po prostu zrobić dopasowanie z użyciem symboli wieloznacznych bez konieczności implementowania algorytmu od początku?

+1

'Pattern.quote()' i reszta twojego podejścia powinien działać dobrze. Co widzisz, co sugeruje coś innego? –

+0

@MattBall Kończę się '\ Q (. *, A) \ E', które nie pasuje do' (foo, a) ', ponieważ' foo' nie pasuje do literału '. *'. –

+0

Myślę, że źle rozumiem, jaki jest ostateczny cel. Otrzymasz ciąg, taki jak '* .wav'. Co się z tym stanie? –

Odpowiedz

8

pomocą prostego wyrażenia regularnego

Jedną z zalet tej metody jest to, że możemy łatwo dodać znaki oprócz * (patrz Dodawanie tokeny na dole).

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

  • Lewa strona | dopasowuje żadnych znaków, które nie są gwiazdą
  • Po prawej stronie rejestruje wszystkie gwiazdy do grupy 1
  • Jeśli Grupa 1 jest pusta: zastąp \Q + mecz + E
  • Jeśli Grupa 1 jest ustawiony: wymień .*

Oto niektóre działające kody (patrz dane wyjściowe online demo).

Wejście: audio*2012*.wav

wyjściowa: \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); 

Dodawanie tokeny

Załóżmy również chcemy przekształcić wieloznaczny ?, który stoi na pojedynczym znakiem, kropką.Po prostu dodać grupę przechwytywania do regex, i wykluczyć go z matchall po lewej stronie:

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

w funkcji zastąpić my, dodać coś takiego:

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

to wygląda najlepiej jak dotąd. czekając, aż ktoś znajdzie jeszcze prostsze rozwiązanie. Dzięki! –

+0

Podoba mi się to, że jeśli chcesz dodać jednoliterowy token '?' Używany w dopasowywaniu symboli wieloznacznych, jest to bułka z masłem: '[^ *?] + | (\ *) | (\?)', A następnie w funkcji replace dodajemy "if (m.group (2)! = null) m.appendReplacement (b,". ");' (jako kropka jest pojedynczym znakiem) – zx81

+0

Nie ma "wildcardSpec.replaceAll (" [^ *] + "," \\\\ Q $ 0 \\\\ E "). ReplaceAll (" \\ * + ",". * ")' Działa również? –

13

Po prostu uciec od wszystkiego - nic z tego nie wyjdzie.

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 

Albo można użyć klas postaci:

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

Łatwiej jest „ucieczka” znaki umieszczając je w klasie znaków, jak prawie wszystkie znaki stracić żadnego szczególnego znaczenia, gdy w klasie znaków . Jeśli nie spodziewasz się dziwnych nazw plików, to zadziała.

+0

Hmm, dlaczego nie pomyślałem o tym. Wydaje się to zbyt łatwe. Dzięki! –

+1

Hmm, niestety to nie działa. Java narzeka "Niedozwolona/nieobsługiwana sekwencja ucieczki w pobliżu indeksu 3 \ f \ o \ o". Wygląda na to, że pozwala on tylko uciec ograniczonemu zestawowi znaków: "Błędem jest użycie odwrotnego ukośnika przed jakimkolwiek alfabetycznym znakiem, który nie oznacza skonstruowanego znaku, są one zarezerwowane dla przyszłych rozszerzeń języka wyrażenia regularnego.". –

+0

Czy skopiowałeś wklej? Ten kod działa bez błędu. Mogę tylko założyć, że kodowałeś 'replaceAll()' zamiast 'replace()' dla drugiego wywołania metody. Czy to się stało? – Bohemian

1

Można również używać znaków ewidencyjnych Quotation: \\Q and \\E - wszystko między nimi jest traktowane jako literalne i nie jest uważane za część wyrażenia regularnego, które ma być ocenione. Tak więc ten kod powinien działać: (?)

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

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

pamiętać, że * wieloznaczny może być również najlepiej dopasowane tylko przeciwko znaków słownych korzystających \ W zależności od tego, jak chcesz, aby wieloznaczny zachowywać

0

Lucene ma klas, które zapewniają tę możliwość, z dodatkowym wsparciem dla ukośnika odwrotnego jako znaku ucieczki. ? dopasowuje pojedynczy znak, 1 dopasowuje 0 lub więcej znaków, \ wymusza następujący znak. Obsługuje punkty kodowe Unicode. Miałem być szybki, ale nie testowałem.

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 Podczas umieszczenia DOS/Windows Path

Wdrażanie znaki Wyznaczona ewakuacyjnych \Q i \E jest chyba najlepszym rozwiązaniem. Ponieważ jednak ukośnik odwrotny jest zwykle używany jako separator plików DOS/Windows, sekwencja "\E" w ścieżce może skutkować parowaniem \Q i . Podczas rozliczania dla * i ? wieloznacznych żetonów, to sytuacja backslashem można rozwiązać w ten sposób:

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

Dwie nowe linie będą dodawane w funkcji Replace z „pomocą prostego Regex "przykład, aby uwzględnić nowy wzorzec wyszukiwania. Kod nadal będzie "przyjazny dla systemu Linux". Jako sposób, to może być napisane tak:

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

Kod wykazać wdrożenie tej metody mogą być napisane tak:

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

Byłoby wygenerowane wyniki:

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