Expressions régulières: existe-t-il un opérateur ET?

708

Évidemment, vous pouvez utiliser le |(pipe?) Pour représenter OR, mais existe-t-il un moyen de représenter ANDégalement?

Plus précisément, je voudrais faire correspondre des paragraphes de texte qui contiennent TOUS une certaine phrase, mais sans ordre particulier.

Hugoware
la source
1
Voulez-vous dire que vous voulez trouver des phrases dans un texte, où chacune de ces phrases est une permutation valide des mots dans une phrase donnée?
Nietzche-jou
2
Je mets cela ici parce que trois ou quatre réponses l'ignorent. Lookahead ne correspond pas à la même longueur pour chaque clause, sauf si elles se terminent par $. Une tête de lecture peut correspondre à quatre caractères et à six autres. Par exemple, (? = A *) (? = Aab) correspondra à aabaaaaba
Zachary Vance
2
essayez d'utiliser uniquement le caractère "espace" pour l'opérateur "ET".
1 I'd like to match paragraphs of text.. 2. Contenant un texte en panne . Le numéro 1 est ouvert à l'interprétation. Le numéro 2 peut être fait de deux manières. Voie 1:, (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}Voie 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)où dans ce cas, l'appariement du paragraphe dans ce cas n'est pas défini jusqu'à ce que la définition du paragraphe soit formalisée.

Réponses:

385

Utilisez une expression régulière non consommatrice.

La notation typique (c'est-à-dire Perl / Java) est:

(?=expr)

Cela signifie "match expr mais après cela, continuez de faire correspondre le point de correspondance d'origine."

Vous pouvez en faire autant que vous le souhaitez, et ce sera un «et». Exemple:

(?=match this expression)(?=match this too)(?=oh, and this)

Vous pouvez même ajouter des groupes de capture à l'intérieur des expressions non consommatrices si vous devez y enregistrer certaines données.

Jason Cohen
la source
3
perl -e "q {quelques trucs et choses} = ~ / (? = certains) (? = trucs) (? = choses) /? print 'yes': print 'no'" imprime 'no'.
Robert P
27
Il convient de mentionner que cet exemple particulier est appelé une affirmation d'anticipation positive. Il a d'autres utilisations que "et". Notez que le texte n'est pas consommé.
strager
7
L'utilisation de (? =) Comme ceci entraîne une expression régulière qui ne peut jamais réussir. Mais il est l'analogue de liaison à |. Le PO a tout simplement tort de penser qu'il résoudra son problème.
Nietzche-jou le
10
perl -e "q {des trucs et des choses} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'"
kriss
3
Pouvez-vous s'il vous plaît ajouter un exemple simple en code perl dans votre réponse?
Pithikos
343

Vous devez utiliser l'anticipation comme l'ont dit certains des autres répondants, mais l'anticipation doit tenir compte des autres caractères entre son mot cible et la position de correspondance actuelle. Par exemple:

(?=.*word1)(?=.*word2)(?=.*word3)

Le .*premier lookahead lui permet de faire correspondre le nombre de caractères dont il a besoin avant d'arriver à "word1". Ensuite, la position de correspondance est réinitialisée et le deuxième moteur de recherche recherche "word2". Réinitialisez à nouveau et la partie finale correspond à "word3"; puisque c'est le dernier mot que vous recherchez, il n'est pas nécessaire qu'il soit dans une impasse, mais cela ne fait pas de mal.

Pour faire correspondre un paragraphe entier, vous devez ancrer l'expression régulière aux deux extrémités et ajouter une finale .*pour consommer les caractères restants. En utilisant la notation de style Perl, ce serait:

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

Le modificateur «m» est pour le mode multiligne; il laisse le ^et $correspondre aux limites de paragraphe ("limites de ligne" en regex-parler). Dans ce cas, il est essentiel de ne pas utiliser le modificateur «s», qui permet au métacaractère point de correspondre aux nouvelles lignes ainsi qu'à tous les autres caractères.

Enfin, vous voulez vous assurer que vous faites correspondre des mots entiers et pas seulement des fragments de mots plus longs, vous devez donc ajouter des limites de mots:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
Alan Moore
la source
8
Exactement bien - il y a aussi un tutoriel à ce sujet! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln
9
Merci beaucoup. * Cela fait une différence
Gennadiy Ryabkin
1
+1 pour une réponse claire et succincte présentant l'une des meilleures utilisations des têtes de recherche (contrairement aux utilisations telles qu'un hack pour compter le pourcentage de correspondance d'un mot de passe). :)
zx81
1
@Liam :. MySQL utilise la saveur POSIX ERE, donc non. Il sacrifie effectivement les fonctionnalités au profit de la performance, ce qui me semble raisonnable. Il y a plus d'informations ici .
Alan Moore
3
remplacer .*par [\s\S]*en javascript si vous avez de nouvelles lignes car .dans le moteur regex de javascript ne correspond pas aux nouvelles lignes et ne peut pas être fait avec des modificateurs
Wesley Smith
41

Regardez cet exemple:

Nous avons 2 regexps A et B et nous voulons faire correspondre les deux, donc en pseudo-code, cela ressemble à ceci:

pattern = "/A AND B/"

Il peut être écrit sans utiliser l'opérateur AND comme ceci:

pattern = "/NOT (NOT A OR NOT B)/"

dans PCRE:

"/(^(^A|^B))/"

regexp_match(pattern,data)
fanjabi
la source
24
C'est vrai en termes de logique formelle, mais ce n'est absolument pas utile ici. Dans les expressions régulières, NOT peut être encore plus difficile à exprimer que AND.
Alan Moore
@marvin_dpr Cela a fonctionné pour moi dans CMake tandis que l'autre suggestion (?=expr)ne fonctionne pas. Cela semble dépendre de la mise en œuvre.
Melebius
38
Ne ^signifie pas "début de chaîne" dans la syntaxe des expressions rationnelles?
Lambda Fairy
3
En regex en général, la ^négation n'est qu'au début d'une classe de caractères. À moins que CMake ne fasse quelque chose de vraiment génial (au point où appeler leur langage de correspondance de motifs "regex" pourrait être considéré comme trompeur ou incorrect), je suppose que le fait que cela a fonctionné pour vous était un accident isolé.
tripleee
29

Vous pouvez le faire avec une expression régulière, mais vous voudrez probablement en avoir d'autres. Par exemple, utilisez plusieurs expressions rationnelles et combinez-les dans une clause if.

Vous pouvez énumérer toutes les permutations possibles avec une expression rationnelle standard, comme ceci (correspond à a, b et c dans n'importe quel ordre):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

Cependant, cela fait une expression rationnelle très longue et probablement inefficace, si vous avez plus de deux termes.

Si vous utilisez une version regexp étendue, comme Perl ou Java, ils ont de meilleures façons de le faire. D'autres réponses ont suggéré d'utiliser une opération d'anticipation positive.

Juha Syrjälä
la source
10
Je ne pense pas que votre approche soit plus inefficace que 3 têtes de recherche avec leur retour en arrière catastrophique. Bien sûr, l'écriture est plus longue, mais notez que vous pouvez facilement générer automatiquement le motif. Notez que vous pouvez l'améliorer pour échouer plus rapidement avec a(bc|cb)|b(ac|ca)|c(ab|ba). Et le plus important, vous pouvez l'utiliser avec toutes les saveurs regex.
Casimir et Hippolyte
27

L'opérateur AND est implicite dans la syntaxe RegExp.
L'opérateur OR doit plutôt être spécifié avec un tuyau.
Le RegExp suivant:

var re = /ab/;

signifie la lettre a ET la lettre b.
Il fonctionne également avec des groupes:

var re = /(co)(de)/;

cela signifie le groupe co ET le groupe de.
Remplacer le (implicite) ET par un OU nécessiterait les lignes suivantes:

var re = /a|b/;
var re = /(co)|(de)/;
Emanuele Del Grande
la source
29
Malheureusement, ce n'est pas ce que le PO a demandé. Cela trouve quelque chose dans cet ordre, alors qu'ils le voulaient dans n'importe quel ordre. Découvrez la réponse de stackoverflow.com/users/20938/alan-moore ci-dessous qui est la bonne.
JESii
1
@JESii merci pour votre point, vous avez raison et j'ai mal compris la question de Hugoware, je me suis concentré en particulier sur sa première phrase. La bonne réponse est une bonne utilisation de l'opérateur d'anticipation, comme l'a écrit AlanMoore. Quoi qu'il en soit, je pense que quelqu'un pourrait trouver ma clarification utile, car elle a déjà été votée, donc je ne jetterais pas tout. Cordialement.
Emanuele Del Grande
13

N'est-il pas possible dans votre cas de faire l'AND sur plusieurs résultats correspondants? en pseudocode

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
user54579
la source
3
Je suis dans une situation où j'ai du code qui est un tableau de données de règles, avec une seule chaîne de correspondance de modèle d'expression régulière pour tester la validité de la règle. Passer à plusieurs tests n'est pas quelque chose que je peux faire dans mon cas, et généralement dans les cas d'autres personnes aussi!
Alan Wolfe
11

Pourquoi ne pas utiliser awk?
avec awk regex ET, OU les choses sont si simples

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
mug896
la source
9

Si vous utilisez des expressions régulières Perl, vous pouvez utiliser l'anticipation positive:

Par exemple

(?=[1-9][0-9]{2})[0-9]*[05]\b

serait un nombre supérieur à 100 et divisible par 5

jpalecek
la source
8

Vous pouvez diriger votre sortie vers une autre expression régulière. En utilisant grep, vous pouvez faire ceci:

grep A | grep B

Éboueur
la source
8

En plus de la réponse acceptée

Je vais vous fournir quelques exemples pratiques qui clarifieront les choses pour certains d'entre vous. Par exemple, disons que nous avons ces trois lignes de texte:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

Voir la démo ici DEMO

Ce que nous voulons faire ici, c'est sélectionner le signe + mais seulement si c'est après deux nombres avec un espace et si c'est avant quatre nombres. Ce sont les seules contraintes. Nous utiliserions cette expression régulière pour y parvenir:

'~(?<=\d{2} )\+(?=\d{4})~g'

Notez que si vous séparez l'expression, cela vous donnera des résultats différents.

Ou peut-être voulez-vous sélectionner du texte entre les balises ... mais pas les balises! Ensuite, vous pouvez utiliser:

'~(?<=<p>).*?(?=<\/p>)~g'

pour ce texte:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

Voir la démo ici DEMO

DevWL
la source
Quelle réponse était la réponse acceptée? Veuillez y ajouter un lien pour l'avenir.
James Brown
6

L'ordre est toujours impliqué dans la structure de l'expression régulière. Pour accomplir ce que vous voulez, vous devrez faire correspondre la chaîne d'entrée plusieurs fois avec différentes expressions.

Ce que vous voulez faire n'est pas possible avec une seule expression rationnelle.

pilif
la source
Ce n'est pas techniquement impossible, mais cela ne vaut pas la peine d'être mis en œuvre. Je ne sais pas pourquoi quelqu'un a voté contre ...
Robert P
13
Probablement parce que ce n'est pas seulement possible, c'est simple, en supposant que votre saveur regex supporte les lookaheads. Et c'est un bon pari; la plupart des principaux langages de programmation actuels les prennent en charge.
Alan Moore
3

Utilisez AND en dehors de l'expression régulière. En PHP lookahead, l'opérateur ne semblait pas fonctionner pour moi, mais j'ai utilisé

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

Le regex ci-dessus correspondra si la longueur du mot de passe est de 3 caractères ou plus et qu'il n'y a pas d'espaces dans le mot de passe.

Hammad Khan
la source