web-dev-qa-db-fra.com

Correspondance générique en Java

J'écris un programme de débogage simple qui prend en entrée des chaînes simples pouvant contenir des étoiles pour indiquer une correspondance générique - tout type

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

Je pensais que je prendrais simplement ce modèle, échapperais à tout caractère spécial d'expression régulière, puis remplacerais tout \\* par .*. Et puis utilisez un matcher d'expression régulière.

Mais je ne trouve aucune fonction Java pour échapper à une expression régulière. La meilleure correspondance que j'ai pu trouver est Pattern.quote, qui ne met toutefois que \Q et \E au début et à la fin de la chaîne. 

Y a-t-il quelque chose en Java qui vous permet simplement de faire cette correspondance générique sans avoir à implémenter l'algorithme à partir de zéro?

9

Utiliser une regex simple

L'un des avantages de cette méthode est que nous pouvons facilement ajouter des jetons en plus de * (voir Ajout de jetons en bas).

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

  • Le côté gauche du | correspond à tous les caractères qui ne sont pas une étoile
  • Le côté droit capture toutes les étoiles du groupe 1
  • Si le groupe 1 est vide, remplacez-le par \Q + Match + E
  • Si le groupe 1 est défini: remplacer par .*

Voici quelques codes de travail (voir le résultat de la démo en ligne ).

Entrée: audio*2012*.wav

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

Ajout de jetons

Supposons que nous voulions également convertir le caractère générique ?, qui représente un seul caractère, par un point. Nous venons d'ajouter un groupe de capture à la regex et de l'exclure du matchall à gauche: 

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

Dans la fonction de remplacement, nous ajoutons quelque chose comme:

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

Juste échapper à tout - aucun mal ne viendra de lui.

    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

Ou vous pouvez utiliser des classes de caractères:

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

Il est plus facile "d'échapper" aux personnages en les plaçant dans une classe de caractères, car presque tous les caractères perdent toute signification particulière dans une classe de caractères. Sauf si vous attendez des noms de fichiers étranges, cela fonctionnera.

13
Bohemian

Il existe une petite méthode utilitaire dans la bibliothèque Apache Commons-IO: org.Apache.commons.io.FilenameUtils # wildcardMatch (), que vous pouvez utiliser sans les subtilités de l'expression régulière.

La documentation de l'API se trouve dans: https://commons.Apache.org/proper/commons-io/javadocs/api-2.5/org/Apache/commons/io/FilenameUtils.html#wildcardMatch(Java.lang. String,% 20Java.lang.String)

6
Marek Gregor

Vous pouvez également utiliser les caractères d'échappement Quotation: \\Q and \\E - tout ce qui les sépare est traité comme un littéral et n'est pas considéré comme faisant partie de l'expression rationnelle à évaluer. Ainsi, ce code devrait fonctionner:

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

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

Notez que votre caractère générique * peut également être mieux adapté uniquement aux caractères Word utilisant\w, en fonction de la manière dont vous souhaitez que votre caractère générique se comporte (?)

1
Matt Coubrough

Regex en tenant compte d'un chemin DOS/Windows

L'implémentation des caractères d'échappement Quotation \Q et \E est probablement la meilleure approche. Cependant, étant donné qu'une barre oblique inverse est généralement utilisée comme séparateur de fichiers DOS/Windows, une séquence "\E" dans le chemin peut affecter le couplage de \Q et de \E. Tout en tenant compte des jetons génériques * et ?, cette situation de la barre oblique inversée peut être résolue de la manière suivante:

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

Deux nouvelles lignes seraient ajoutées à la fonction de remplacement de l'exemple "Using A Simple Regex" pour prendre en charge le nouveau modèle de recherche. Le code serait toujours "compatible Linux". En tant que méthode, cela pourrait être écrit comme ceci:

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

Le code pour démontrer la mise en œuvre de cette méthode pourrait être écrit comme ceci:

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

Ce sont les résultats généré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 a des classes qui offrent cette capacité, avec un support supplémentaire pour la barre oblique inverse en tant que caractère d'échappement. ? correspond à un seul caractère, 1 à 0 ou plus caractères, \ échappe au caractère suivant. Prend en charge les points de code Unicode. Censé être rapide mais je n'ai pas testé.

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