Expression régulière et capture

Résolu
Miraaa16 - 14 mai 2023 à 04:28
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 - 25 mai 2023 à 16:46

Bonjour tout le monde !

Cela maintenant plus de 6H que je suis bloquée sur une expression régulière, pourtant elle semble si simple...

Je cherche à faire une expression régulière qui me permet de trouver un mot d'au moins 4 caractères et qui se répète au moins 5 fois sur la même ligne.

J'ai fais ce code qui n'a pas marché

(\b\w{4,}\b)(\b\1\b){4,}

J'ai essayé plusieurs autre code dont je ne me souviens plus qui n'ont pas marché :/ . 

Est-ce que quelqu'un peut me donner un coup de pouce ?

Et aussi m'expliquer comment fonctionne l'expression "\1" . Je croyais qu'il permettait de capturer la première expression trouvée, mais je ne suis plus sûre de comprendre son usage.

Je ne sais pas si c'est le forum approprié pour parler d'expression régulière, si ce n'est pas le cas merci de me référer le forum sur lequel je dois publier la question.

Je vous remercie d'avance de votre aide !

A voir également:

9 réponses

Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
14 mai 2023 à 10:42

Bonjour

@Pierrot, il ne faut pas commencer par ^ car le mot de 4 lettres n'est pas forcément au début de la phrase, de plus Miraaa s'interroge sur \1 et pas \b.

@Miraaa, il existe des sites permettant de tester visuellement les regex, il faut choisir le site en fonction du moteur que tu utilises, car il peut y avoir des subtilités de l'un à l'autre.

Pour .Net (C#, Vb.Net, F#, ASP.Net etc...), je te conseille regexstorm.net.

Là j'ai choisi regex101.com, car il permet d'enregistrer différentes versions de sa regex, j'ai laissé le moteur par défaut, soit celui qui est avec PHP > 7.3

Dans un premier temps, capturons tous les mots de 4 lettres et plus

https://regex101.com/r/TSCEAn/1

Maintenant essayons de capturer 2 mots consécutifs de 4 lettres et plus, 1er essai naïf

https://regex101.com/r/TSCEAn/2

Ca ne marche pas, car il y a un espace, une virgule etc entre mes mots.

Donc essayons maintenant, 2 mots consécutifs séparés par un espace

https://regex101.com/r/TSCEAn/3

Et là, on arrive à un résultat mitigé, "bonjour bonjour" et "lequel bonjour" sont capturés, mais pas les autres: "test dans" par exemple. Quand je te parlais de subtilités, là, on en touche peut-être une.


Quoi qu'il en soit, ton souci avec \1 était du même ordre, \1 permet effectivement de capture, une nouvelle fois, le résultat du groupe 1. Je poursuis donc la création de ma regex, en demandant qu'il y ait des caractères, peu importe lesquels et leur nombre, tant que ce nombre est le plus petit possible

https://regex101.com/r/TSCEAn/4

Là, on a 3 captures,

  • "bonjour bonjour", avec le groupe 1 "bonjour"
  • "test dans lequel bonjour est écrit cinq fois et test", et donc le groupe 1 "test"
  • "bonjour test, bonjour", avec à nouveau "bonjour"

Il ne reste plus qu'à généraliser

https://regex101.com/r/TSCEAn/5

(\b\w{4,}\b)(.*?\1){4,}

Tu n'étais pas loin, il fallait 

  • penser au "trou" entre les mots
  • ne pas ajouter \b de part et d'autre de \1, car le premier groupe les contient déjà

Si par cas, tu ne veux pas de groupe 2, tu peux écrire

https://regex101.com/r/TSCEAn/6

(\b\w{4,}\b)(?:.*?\1){4,}

2

Merci beaucoup !

J'avais aussi pensé à quelque chose de similaire sauf que dans mon cas je dois seulement trouver les mots concernés sans les caractères et les mots qui le suivent.

J'ai également essayé ce code, celui là les retrouve mais malheureusement il ignore le dernier mot, je ne sais pas pourquoi.

(\b(\w{4,})\b)(?=(?:.*?\b\1\b){3,})
0

As-tu essayé:
^\b(\w{4,})(\b\1){4,}
Qu'est-ce qu'un mot pour toi?
Si ça ne marche pas, tu pourrais remplacer le \b par l'absence des caractères désirés ( [^...] )
et le\w par ces caractères.

Par exemple: \b = [^A-Za-z] et \w{4,} = [A-Za-z][a-z]{3,}

Le premier \b pourrait être suivi de  *  et l'autre de  +  (équivalent à {1,} )

Le problème est la compréhension du \b. Voici ce que j'ai trouvé:

http://www2.univ-ag.fr/dsi/fichiers/d/cours-regex.pdf

1
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
14 mai 2023 à 12:04

Avec ma regex, le mot concerné est récupérable dans le groupe 1, donc tu as accès à la donnée que tu cherches.

Et à priori, elle devrait fonctionner pour la majorité des moteurs (celui de vbscript ou vba pas sûr).


Ce que tu as écrit ne peux pas marcher pour plusieurs raisons

  • la numération {4,} est en dehors du suffixe
  • tu as laissé les \b de part et d'autre de \1
  • certains moteurs n'acceptent pas les suffixes à taille variable.

En corrigeant les 2 premiers points, toujours sur le moteur PHP > 7.3, cela fonctionne

https://regex101.com/r/TSCEAn/7

(\b\w{4,}\b)(?=(.*?\1){4,})

Par contre, elle ne fonctionne ni avec celui de Golang, ni avec celui de Rust, probablement à cause du préfixe à taille variable.


1

Malheureusement, cette expression prend en compte certains caractères qui viennent avant le mot capturé. J'avais aussi tenté de résoudre cela avec "?:" qui permet(si je comprends bien) de signifier que les caractères qui viennent après ne doit pas être capturé

(\b\w{4,}\b)(?=(?:.*?\1){4,})

Mais là également certains mots recherchés ne sont pas sélectionnée. Peut-être "?=" n'est pas l'expression approprié.

https://regex101.com/r/SMDCZD/1

0
PierrotLeFou
14 mai 2023 à 18:55

Un point embêtant est que les regex ne semblent pas avoir de standard (pas tout à fait)
Par exemple, le '+' qui signifie {1,} n'est pas accepté partout.
Il me semble que même sur un système comme Unix, c'était différent pour sed et grep
Je suis sur Windows 10 et j'utilise une version de GNU sed adaptée à Windows.
L'expression suivante marche pour: marc marcmarcmarcmarc
(\b\w{4,}\b)(.*\1){4,}
Si je suis le '.' de '+' au lieu de '*', ça ne marche plus
On peut précéder la séquence de 5+ mots d'un autre mot, ou méeme intercaler un autre mot.

1
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
14 mai 2023 à 19:05

Si je suis le '.' de '+' au lieu de '*', ça ne marche plus

c'est normal, et pour 2 raisons:

  •  * veut dire {0,} et tes marc sont ne sont pas séparés 
  • \b est une "frontière" de mot, et du coup marcmarcmarcmarc ce n'est qu'un seul mot
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
15 mai 2023 à 17:26

Bonjour,

Quelques éléments pour alimenter la discussion et tenter de répondre à certaines interrogations encore en suspens.

Tout d'abord, pour qu'on puisse répondre il faudrait aussi clarifier l'énoncé, car selon cette information. En effet, une expression régulière au sens strict n'est pas capable de compter ni de mémoriser et donc de répondre à ton énoncé. Cette limitation est inhérente à la définition d'une expression régulière (= opération rationnelle), qui se base sur un automate fini non déterministe (NFA), et qui n'a donc pas de mémoire. Cela signifie que dans le cas général, ton problème n'a juste pas de solution.

Opérateurs étendus

Toutefois, certains moteurs d'expression régulières ont été enrichis et supportent une mémoire, et par la même occasion l'éventail d'opérateurs qu'ils supportent. En particulier, les opérateurs {m,}, {m}, {m,n}, {,n} pour ce souvenir qu'on a déjà vu un sous motif et reposent donc sur une notion de mémoire. Plus précisément, le NFA est enrichi de sorte à mémoriser combien de fois on a traversé chaque état du NFA. On peut remarquer que pour certains de ces opérateurs étendus, certains peuvent être réécris sans nécessiter de mémoire.

Exemples :

  • Concernant +, on pourrait s'en sortir en réécrivant "r+" en "rr*". Cela engendre cependant un automate plus grand.
  • Concernant {m,} on pourrait s'en sortir en réécrivant "r{3,} en "rrrr*"

Motifs prédéfinis

Ceci dit, ce n'est pas la seule chose qui n'est pas standard dans un certain nombre de propositions faites dans ce fil de discussion. Par exemple, les motifs \b, \w, \d, etc ne son pas supportés par tous les moteurs d'expression régulières. Ici il est possible de ruser (par exemple \d se réécrit [0-9])

Lookahead et lookbehind

Les opérateurs lookahead et lookbehind sont encore plus spéciaux, car eux se basent carrément sur une notion de contexte.

Nuances entre les moteurs d'expressions régulières

Typiquement, grep n'implémente que les opérations de bases, tandis que sa version étendue egrep les supporte tous. Cela signifie qu'avec egrep, on pourra écrire plus simplement certaines expression régulières qu'avec grep, voir définir des expression régulières étendues qu'on ne pourrait pas retranscrire en se limitant aux opérateurs de grep.

Ceci n'est qu'un exemple de différences. Pour en savoir plus, voir cet article qui discute des expression régulières POSIX et PCRE.

Les références arrières (\1, \2, ...)

Dans des outils comme sed (et apparemment egrep) \1 (et de la même manière \2, \3, etc) correspondent au bloc de texte capturé par une sous-expression régulière entourée de parenthèses.

Exemple : ici on capture avec \(...\)  le bloc "aaaa". À chaque fois qu'on le détecte, on sait que \1 correspond à "aaaa".

echo "zzzz aaaa bbbb aaaa cccc aaaa dddd aaaa eeee aaaa yyyy" | sed -e "s/\(aaaa\)/xx\1xx/g"

Résultat :

zzzz xxaaaaxx bbbb xxaaaaxx cccc xxaaaaxx dddd xxaaaaxxa eeee xxaaaaxx yyyy

Chaque bloc capturé est numéroté par ordre d'apparition (par rapport à la parenthèse gauche) dans l'expression régulière de départ. Ainsi, il est parfaitement possible de capturer des sous blocs dans un bloc lui-même capturé.

grep vs sed

Un autre problème dans ce que tu veux faire, c'est détecter un bloc qui implique un mot w de 4 caractères répété au moins 5 fois, mais en réalité, tu ne veux pas retourner ce bloc, mais le mot w. C'est un peu contradictoire avec ce que fait grep : il retourne la sous-chaîne qui concorde avec l'expression régulière donc tout le bloc et pas seulement le mot w.

Si tu ne veux retourner que w, il faudrait par exemple utiliser sed et substituer l'ensemble de la chaîne d'entrée par \1. Dans certains langage (genre awk), les opérations réalisables par sed correspondent aux fonctions sub ou gsub.

Exemple simpliste : ici on remplace la chaîne d'entrée par son premier mot d'au moins 4 caractères :

echo "zzzz aaaa bbbb aaaa cccc aaaa dddd aaaa eeee aaaa yyyy" | sed -e "s/.*\(\b\w\{4,\}\b\).*/\1/g"
aaaa

Remarque : note qu'avec sed, il faut échapper les accolades de l'opérateur {m,} et les parenthèses qui capturent.

Autre exemple : on met entre crochets tous les mots d'au moins 4 caractères :

echo "zzzz aaaa bbbb aaaa cccc aaaa dddd aaaa eeee aaaa yyyy" | sed -e "s/\(\b\w\{4,\}\b\)/[\1]/g"
[zzzz] [aaaa] [bbbb] [aaaa] [cccc] [aaaa] [dddd] [aaaa] [eeee] [aaaa] [yyyy]

Remarque : le "/g" en fin d'expression régulière signifie que la substitution doit être faite pour chaque occurrence trouvée, pas seulement la première.

Il faudrait vérifier si tu as le droit de parler de \1 dans un la définition du motif d'une expression régulière.

Bonne chance

1
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
14 mai 2023 à 14:53

(?:....) ....signifiant un pattern quelconque veut dire que ce pattern, bien qu'entre parenthèses, ne constituera pas un groupe, mais il fait partie de la capture s'il n'est pas dans un suffixe (ou un préfixe)

Je ne comprends pas 

Malheureusement, cette expression prend en compte certains caractères qui viennent avant le mot capturé

Le problème dans l'exemple que tu montres, c'est qu'il y a beaucoup plus que 5 répétitions et donc il est possible de capturer le 2e Bonjour, le 3e etc... 

Et ça, c'est dû au suffixe, car il ne fait pas partie de la capture, donc il peut être réutilisé ou capturé par l'occurrence suivante.

Pour que ça n'arrive pas, il faut revenir à ce que je t'ai proposé initialement, une capture globale et le mot dans le groupe 1.

https://regex101.com/r/SMDCZD/2

(\b\w{4,}\b)(?:.*?\1){4,}

À noter que là, ça s'arrête au saut de ligne, si on active l'option "singleline",

https://regex101.com/r/SMDCZD/3

Ça capture jusqu'au dernier Bonjour, mais Monument n'est plus accessible.


0
PierrotLeFou
15 mai 2023 à 03:18

Ma remarque au sujet du * ou du + n'était pas une question mais un commentaire pour miraa.
Avec la forme suivante je peux éviter les auttres mots intercalés tels que:
marc marc marc jade marc marc
(\b\w{4,}\b)(\W\1({4,}

Le W majuscule au lieu du w minuscule inverse la condition.

La forme [^\w] ne marche pas. Ici je ne sais pas, est-ce que le \w n'est pas interprété?

0
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
Modifié le 15 mai 2023 à 08:18

Ta regex ne marche pas l'exemple de Miraaa.

Peut-être que ton système vocal ne te permet pas d'utiliser regex101.

Mais du coup, je pense que tes interventions embrouillent plus la situation qu'elles ne la démêlent

0
PierrotLeFou
15 mai 2023 à 19:03

Ce n'est pas moi qui crée la confusion, mais les logiciels qui utilisent les expressions régulières.
Par exemple, avec sed, si j'utilise l'option -E, je change la règle.
>echo aaa | sed -n "/a{3}/p"                                                   
                                                                                                                        
>echo aaa | sed -E -n "/a{3}/p"                                                
aaa                                                                                                                     
                                                                                                                        
>
Avec l'option -E, sed utilise les expressions dites "avancées" où les { par exemple n'ont pas besoin d'être échappées si elles sont des éléments syntaxiques.
J'ai déjà eu en main une vieille version de sed / regex venant de la FSF au MIT et je l'avais modifiée.
Elle n'était plus compatible dans ses extensions avec aucun autre logiciel utilisant les expressions régulières.

0

Merci à tous pour vos précieuses aides !

Ce forum m'a beaucoup aidé à clarifier certains aspects sur les expressions régulières.

0
Whismeril Messages postés 19029 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 26 avril 2024 931
25 mai 2023 à 16:46

De rien 

0