web-dev-qa-db-fra.com

Correspondance générique d'expressions régulières

J'ai une liste d'environ 120 000 mots anglais (essentiellement tous les mots de la langue).

J'ai besoin d'une expression régulière qui permettrait de chercher dans ces mots en utilisant des caractères génériques, a.k.a. * et ?.

Quelques exemples:

  • si l'utilisateur recherche m?st*, il correspondrait par exemple à master ou mister ou mistery.
  • si l'utilisateur recherche *ind (tout mot se terminant par ind), il correspondrait à wind ou bind ou blind ou Grind.

Maintenant, la plupart des utilisateurs (en particulier ceux qui ne sont pas familiers avec les expressions régulières) savent que ? remplace exactement 1 caractère, tandis que * remplace plutôt 0, 1 caractère ou plus. Je veux absolument construire ma fonction de recherche sur cette base.

Ma question est la suivante: comment convertir ce que l'utilisateur tape (m?st* par exemple) en expression régulière?

J'ai cherché sur le Web (y compris évidemment ce site) et tout ce que j'ai pu trouver, ce sont des tutoriels qui tentent de m'apprendre trop ou des questions qui sont un peu similaires, mais pas assez pour fournir une réponse à mon propre problème.

Tout ce que je pouvais comprendre, c'est que je dois remplacer ? par .. Donc, m?st* devient m.st*. Cependant, je n'ai aucune idée de quoi remplacer * par.

Toute aide serait grandement appréciée. Je vous remercie.

PS: Je suis totalement nouveau dans les expressions régulières. Je sais à quel point ils peuvent être puissants, mais je sais aussi qu'ils peuvent être très difficiles à apprendre. Donc, je n'ai jamais pris le temps de le faire ...

19
Radu Murzea

À moins que vous souhaitiez un comportement amusant, je vous recommanderais d'utiliser \w au lieu de .

. fait correspondre les espaces et autres symboles non-Word, ce que vous ne voudrez peut-être pas faire.

Donc, je voudrais remplacer ? par \w et remplacer * par \w*

De même, si vous souhaitez que * corresponde à au moins un caractère, remplacez-le par \w+. Cela voudrait dire que ben* correspondrait à bend et bending mais pas ben - à vous de choisir, cela dépend simplement de vos besoins.

15
gnomed

Jetez un oeil à cette bibliothèque: https://github.com/alenon/JWildcard

Il enveloppe toutes les parties spécifiques non génériques avec des guillemets regex, ainsi aucun traitement de caractère spécial requis: Ce générique:

"mywil?card*"

sera converti en cette chaîne de regex:

"\Qmywil\E.\Qcard\E.*"

Si vous souhaitez convertir un caractère générique en chaîne regex, utilisez:

JWildcard.wildcardToRegex("mywil?card*");

Si vous souhaitez vérifier la correspondance directement, vous pouvez utiliser ceci:

JWildcard.matches("mywild*", "mywildcard");

Les règles génériques par défaut sont "?" -> ".", "" -> ".", mais vous pouvez modifier le comportement par défaut si vous le souhaitez, en définissant simplement les nouvelles règles.

JWildcard.wildcardToRegex(wildcard, rules, strict);

Vous pouvez utiliser des sources ou le télécharger directement à l’aide de maven ou gradle de Bintray JCenter: https://bintray.com/yevdo/jwildcard/jwildcard

Façon Gradle:

compile 'com.yevdo:jwildcard:1.2'

Façon Maven:

<dependency>
  <groupId>com.yevdo</groupId>
  <artifactId>jwildcard</artifactId>
  <version>1.2</version>
  <type>pom</type>
</dependency>
7
lenon

Remplacez ? par . et * par .*.

6
NPE

Voici un moyen de transformer un caractère générique en regex:

  1. Ajoutez tous les caractères spéciaux([{\ ^ - = $! |]}). + Avec\- afin qu'ils correspondent sous forme de caractères et ne rendent pas l'expérience utilisateur inattendue. Vous pouvez également le placer entre \ Q (qui commence la citation) et \ E (qui le termine). Voir également le paragraphe sur la sécurité.
  2. Remplacez * le caractère générique par \ S *
  3. Remplacer? joker avec \ S?
  4. Facultatif: préfixez le motif avec ^ - ceci forcera la correspondance exacte avec le début.
  5. En option: ajouter $ au motif - cela forcera la correspondance exacte avec la fin.

    \ S - représente un caractère non-espace, qui se produit zéro ou plusieurs fois.

Considérez en utilisant des quantificateurs réticents (non gloutons) si vous devez faire correspondre des caractères après * ou +. Cela peut être fait en ajoutant ? après * ou + comme ceci: \ S *? et \ S * +?

Considérez security: l'utilisateur vous enverra du code à exécuter (parce que regex est aussi une sorte de code et que la chaîne utilisateur est utilisée comme regex). Vous devez éviter de transmettre des expressions rationnelles non masquées à d'autres parties de l'application et les utiliser uniquement pour filtrer les données récupérées par d'autres moyens. Parce que si vous le faites, l’utilisateur peut affecter la vitesse de votre code en fournissant différentes expressions rationnelles avec une chaîne générique - cela pourrait être utilisé dans les attaques par déni de service.

Exemple pour montrer les vitesses d'exécution de modèles similaires:

seq 1 50000000 > ~/1
du -sh ~/1
563M
time grep -P '.*' ~/1 &>/dev/null
6.65s
time grep -P '.*.*.*.*.*.*.*.*' ~/1 &>/dev/null
12.55s
time grep -P '.*..*..*..*..*.*' ~/1 &>/dev/null
31.14s
time grep -P '\S*.\S*.\S*.\S*.\S*\S*' ~/1 &>/dev/null
31.27s

Je suggérerais de ne pas utiliser. * Simplement parce que cela peut correspondre à tout et que les choses sont généralement séparées par des espaces.

6
Bohdan
  1. Remplace tout '?' caractères avec '\ w'
  2. Remplacer tous les caractères '*' par '\ w *'

L'opérateur '*' répète l'élément précédent '.' (n'importe quel caractère) 0 fois ou plus.

Cela suppose qu'aucun des mots ne contient '.', '*' Et '?'. 

C'est une bonne référence

http://www.regular-expressions.info/reference.html

2
Thevenin

. est une expression qui correspond à à n'importe quel caractère, comme vous l'avez découvert. Au cours de vos heures de recherche, vous avez sans doute aussi trébuché sur *, qui est un opérateur de répétition qui, lorsqu'il est utilisé après qu'une expression correspond à l'expression précédente zéro ou plusieurs fois dans une ligne.

Donc, l'équivalent de votre signification de * est la réunion de ces deux éléments: .*. Cela signifie alors "n'importe quel caractère zéro ou plusieurs fois".

Voir le Didacticiel Regex sur les opérateurs de répétition .

1
Mark Peters

Remplacez * par .* (l'équivalent rationnel de "0 ou plus de tout caractère").

1
Amber

C'est ce que j'utilise:

String wildcardToRegex(String wildcardString) {
    // The 12 is arbitrary, you may adjust it to fit your needs depending
    // on how many special characters you expect in a single pattern.
    StringBuilder sb = new StringBuilder(wildcardString.length() + 12);
    sb.append('^');
    for (int i = 0; i < wildcardString.length(); ++i) {
        char c = wildcardString.charAt(i);
        if (c == '*') {
            sb.append(".*");
        } else if (c == '?') {
            sb.append('.');
        } else if ("\\.[]{}()+-^$|".indexOf(c) >= 0) {
            sb.append('\\');
            sb.append(c);
        } else {
            sb.append(c);
        }
    }
    sb.append('$');
    return sb.toString();
}

Liste de caractères spéciaux de https://stackoverflow.com/a/26228852/1808989 .

0
Andrew Sun
function matchWild(wild,name)
{
    if (wild == '*') return true;

    wild = wild.replace(/\./g,'\\.');
    wild = wild.replace(/\?/g,'.');
    wild = wild.replace(/\\/g,'\\\\');  
    wild = wild.replace(/\//g,'\\/');
    wild = wild.replace(/\*/g,'(.+?)');

    var re = new RegExp(wild,'i');
    return re.test(name);
}
0
Clif