web-dev-qa-db-fra.com

Expression régulière pour trouver deux chaînes n'importe où dans l'entrée

Comment écrire une expression régulière pour faire correspondre deux chaînes données, quelle que soit leur position dans la chaîne?

Par exemple, si je recherche cat et mat, il devrait correspondre:

The cat slept on the mat in front of the fire.
At 5:00 pm, I found the cat scratching the wool off the mat.

Peu importe ce qui précède ces chaînes.

37
Phanindra K
/^.*?\bcat\b.*?\bmat\b.*?$/m

Utilisation du modificateur m (qui garantit que les métacaractères de début/fin correspondent aux sauts de ligne plutôt qu'au tout début et à la fin de la chaîne):

  • ^ correspond à la ligne commençant
  • .*? correspond à quoi que ce soit sur la ligne avant ...
  • \b correspond à une limite de mot la première occurrence d'une limite de mot (comme discuté avec @codaddict)
  • puis la chaîne cat et une autre limite de Word; notez que les traits de soulignement sont traités comme des caractères "Word", donc _cat_ serait pas match *;
  • .*?: tous les caractères avant ...
  • mat, limite
  • .*?: tous les caractères restants avant ...
  • $: la fin de la ligne.

Il est important d'utiliser \b pour s'assurer que les mots spécifiés ne font pas partie de mots plus longs, et il est important d'utiliser des caractères génériques non-gloutons (.*?) et gloutons (.*), car ce dernier échouerait dans les chaînes telles que "Il y a un chat en haut du tapis qui est sous le chat. " (Cela correspondrait à la dernière occurrence de "chat" plutôt qu'à la première.)

* Si vous voulez être capable de correspondre à _cat_, vous pouvez utiliser:

/^.*?(?:\b|_)cat(?:\b|_).*?(?:\b|_)mat(?:\b|_).*?$/m

qui correspond aux caractères soulignés ou / limites de mots autour des mots spécifiés. (?:) indique un groupe sans capture, ce qui peut améliorer les performances ou éviter les captures en conflit.

Edit: Une question a été soulevée dans les commentaires sur le point de savoir si la solution fonctionnerait pour des phrases plutôt que pour des mots. La réponse est, absolument oui. Ce qui suit correspondrait à "Une ligne qui comprend à la fois la première phrase et la deuxième phrase":

/^.*?(?:\b|_)first phrase here(?:\b|_).*?(?:\b|_)second phrase here(?:\b|_).*?$/m

Edit 2: Si l'ordre n'a pas d'importance, vous pouvez utiliser:

/^.*?(?:\b|_)(first(?:\b|_).*?(?:\b|_)second|second(?:\b|_).*?(?:\b|_)first)(?:\b|_).*?$/m

Et si les performances sont vraiment un problème ici, il est possible que le lookaround (si votre moteur regex le supporte) fonctionne (mais ne le fera probablement pas) mieux que ce qui précède, mais je vais laisser à la fois la version de lookaround plus complexe et les tests de performance un exercice à l'interrogateur/lecteur.

Edité par le commentaire de @Alan Moore. Je n'ai pas eu l'occasion de le tester, mais je vais prendre votre parole pour cela.

59
eyelidlessness
(.* Word1.* Word2.* )|(.* Word2.* Word1.*)
19
Johan

Tu peux essayer:

\bcat\b.*\bmat\b

\b est un anchor et correspond à une limite Word. Il recherchera words cat et mat n'importe où dans la chaîne avec mat suivant le chat. Il ne correspondra pas:

Therez caterpillar on the mat

mais correspondra 

The cat slept on the mat in front of the fire

Si vous voulez faire correspondre des chaînes qui ont lettres cat suivi de mat, vous pouvez essayer:

cat.*mat

Cela correspondra aux deux exemples de chaînes ci-dessus.

4
codaddict

Si vous devez absolument utiliser une seule expression rationnelle, alors 

/(?=.*?(string1))(?=.*?(string2))/is

modificateur i = insensible à la casse

. *? Evaluation paresseuse pour n'importe quel personnage (correspond le moins possible)

? = pour LookAhead positif, il doit correspondre quelque part

le modificateur s =. (point) accepte également les sauts de ligne

3
Kevin Johnson

C'est assez facile sur la puissance de traitement requise:

(string1(.|\n)*string2)|(string2(.|\n)*string1)

Je l'ai utilisé dans Visual Studio 2013 pour trouver tous les fichiers contenant les chaînes 1 et 2.

2
Mike Socha III

vous n'êtes pas obligé d'utiliser regex. Dans votre langue préférée, divisez les espaces en deux, passez en revue les mots séparés, vérifiez si vous avez un chat ou une natte. par exemple en Python

>>> for line in open("file"):
...     g=0;f=0
...     s = line.split()
...     for item in s:
...         if item =="cat": f=1
...         if item =="mat": g=1
...     if (g,f)==(1,1): print "found: " ,line.rstrip()

found:  The cat slept on the mat in front of the fire.
found:  At 5:00 pm, I found the cat scratching the wool off the mat.
1
ghostdog74

Cela fonctionne pour la recherche de fichiers contenant à la fois String1 et String2.

(((. | n)) chaîne1 ((. |\n) ) chaîne2) | (((. | n)) chaîne2 ((. | n) ) chaîne1)

Faites correspondre n'importe quel nombre de caractères ou de champs de ligne Suivi de Chaîne1 Suivi d'un nombre quelconque de caractères ou de champs de ligne Suivi de Chaîne2 OU Faites correspondre un nombre quelconque de caractères ou de champs de ligne suivi de String2 suivi d'un nombre quelconque de caractères ou de champs de ligne suivi de String1

0
Don