IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Problème de Regex


Sujet :

Java

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    Points : 71
    Points
    71
    Par défaut Problème de Regex
    Bonjour,

    Je fais appel à vous car j'ai besoin de vos conseils pour les expressions régulières.

    Dans un travail, je tente de trouver toutes les lignes contenant les mots dog et cat (peu importe l'endroit, l'ordre, pluriel et le première lettre majuscule).

    Cela, j'y arrive avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?=.*\b[dD]ogs??\b)(?=.*\b[cC]ats??\b)^.*$
    Ensuite, je dois faire le même exercice mais en capturant seulement les mots de 3 lettres et plus entre les mots dog et cat avec les mêmes spécifications que précédemment. Et là, je bloque.

    J'arrive à extraire le texte entre les mots de cette façon:

    J'ai alors pensé que je pouvais ensuite remplacer:

    Pour ensuite être en mesure de boucler dans le groupe mais sans succès...

    Pouvez-vous m'aider?

    Merci!

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Les regex ne sont pas un langage de programmation, elles ne vont pas "capturer" une liste de mots. Elles captureront autant de parenthèses que tu as mises dedans, ni plus ni moins.
    Commence par récupérer ce qu'il y a entre tes deux mots. Puis, applique un autre traitement dessus pour trouver les mots de 3 lettres et plus. (Genre un autre find() avec une autre regex.)

    Au passage, je saisis mal l'intérêt de (?<=dog)(.*?)(?=cat) par rapport à dog(.*?)cat passablement plus simple.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    En temps normal, la manière la plus simple consiste à d'abord chercher \b[Cc]at\b(.*?)\b[Dd]og\b (ou l'inverse) puis dans un deuxième temps de chercher les mots de trois lettres et plus dans chaque groupes de capture.

    Pour réaliser l'opération en une seule expression, il faut utiliser la méthode find() et l'ancre \G. L'ancre \G marque soit le début de la chaîne de caractère (ce qui ne nous intéresse pas ici) soit la position après la dernière correspondance. L'intérêt de cette ancre est de permettre la contiguïté des correspondances lors de recherches successives.

    Dans ce but on commence la pattern avec deux points d'entrée possible:
    • soit avec \G pour obtenir un résultat collé au précédent (contigu donc).
    • soit avec le mot cat


    Ce qui donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?:\G(?!\A)|\b[Cc]at\b)
    (remarque que pour éviter que \G matche le début de la chaîne on ajoute (?!\A).)

    Ensuite il ne reste plus qu'à décrire ce qui vient après, jusqu'au mot dog (exclu, car sinon la recherche continuera au-delà du mot dog) ou jusqu'au prochain mot de trois lettres (inclus cette fois ci pour permettre la contiguïté avec l'occurrence suivante):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?:\G(?!\A)|\b[Cc]at\b)(?:[^\w\r\n]+\w{1,2}\b)*+[^\w\r\n]+(?:(?=([Dd]og)\b)|(\w+))
    (?:[^\w\r\n]+\w{1,2}\b)*+ décrit tout ce qui n'est pas un mot de trois lettres ou plus pouvant se trouver avant le mot de trois lettre ou plus ou le mot dog. La classe de caractère [^\w\r\n] contient tout ce qui n'est pas un "word character" ou un CRLF pour ne pas sortir de la ligne. J'utilise un quantificateur possessif pour interdire tout retour en arrière une fois le groupe matché.

    (?:(?=([Dd]og)\b)|(\w+)) permet de capturer deux choses, soit le mot dog pour s'assurer qu'il est bien présent sur la ligne, soit le mot de trois lettres ou plus. (NB: il est inutile de contrôler le nombre de caractères de par l'utilisation du quantificateur possessif précédemment)

    Pour améliorer les performances de cette pattern on peut mettre en facteur le \b au début ce qui évitera des tests inutiles au moteur de regex:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    \b(?:\G(?!\A)|[Cc]at\b)(?:[^\w\r\n]+\w{1,2}\b)*+[^\w\r\n]+(?:(?=([Dd]og)\b)|(\w+))
    L'analyse des résultats de find() est simple. Les mots de trois lettres et plus sont dans le deuxième groupe de capture. Dés que celui ci est null, il suffit de vérifier que le premier groupe de capture (celui qui matche "dog") existe bien.

    Il est possible de modifier cette pattern pour qu'elle trouve indifféremment les séquences commençant par "cat" ou "dog" en ajoutant un test avant (lookahead) au deuxième point d'entrée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    \b(?:\G(?!\A)|([Cc]at|[Dd]og)\b(?=(?>[^\ncCdD]+|\B[CcDd]|[Cc](?!at\b)|[Dd](?!og\b))+(?!(?i)\1)\b(?:[Dd]og|[Cc]at)\b))(?:[^\w\r\n]+\w{1,2}\b)*+[^\w\r\n]+(?:(?=[Cc]at\b|[Dd]og\b)|(\w+))
    Ce test avant vérifie qu'il n'y a pas d'autre "cat" ou "dog" jusqu'au prochain "cat" ou "dog" qui doit être différent du mot de départ. À noter que le groupe de capture à la fin a été enlevé. Il n'est plus nécessaire dés lors que l'on a testé dés le départ la présence de "cat" ou "dog" à la fin.
    Cette méthode avec le test avant à le désavantage de faire parcourir deux fois la même portion de chaîne au moteur de regex (une fois dans le test avant et une autre fois lors des entrées successives par la branche \G.)

    On peut lui préférer une approche plus simple qui consiste à utiliser:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    \b(?:\G(?!\A)|([Cc]at|[Dd]og)\b)(?:[^\w\r\n]+\w{1,2}\b)*+[^\w\r\n]+(?:(?=([Dd]og|[Cc]at)\b)|(\w+))
    . L'analyse des résultats diffère alors un peu. Il y a maintenant trois groupes de capture:
    • Groupe 1: Le mot de départ ("cat" ou "dog")
    • Groupe 2: Le mot de trois lettres ou plus
    • Groupe 3: Le mot d'arrivée

    Il faut donc stocker le mot du groupe 1 et le comparer sans prendre en compte la casse avec celui récupérer plus tard dans le groupe 3.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    Points : 71
    Points
    71
    Par défaut
    Bonsoir,

    Désolé du délais de réponse de ma part. Je vous remercie pour vos réponses.

    Malheureusement, c'est un travail que je dois faire et cela doit être avec une seule expression régulière. Je commence à me demander s'il ne s'agit pas d'une question piège?

    Le code de CosmoKnacki ne semble pas fonctionner...

    Merci de votre aide, je galère...

  5. #5
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Si le code fonctionne, tu peux d'ailleurs le tester ici: http://fiddle.re/bgy6p (clique sur Java)

    Peut-être n'as tu pas pensé à doubler les antislashes, ou à sortir de la boucle du find().

    Par contre je te laisse le soin de gérer les "s".
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Une autre méthode consiste à utiliser un lookbehind de taille variable mais limitée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?<=\b(cat|dog)s?\b.{1,1000}?)\b\w{3,}\b(?=.*?\b(?!\1s?\b)(?:dog|cat)s?\b)
    Mais cette méthode à l'inconvénient de présumer de la taille maximum de la ligne.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  7. #7
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 551
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Le code fonctionne, mais il fait une utilisation inhabituelle de find(), éloignée de la manière dont on utilise une regex habituellement.
    S'il a comme contrainte de devoir forcément utiliser une regex, il a probablement aussi comme contrainte de l'utiliser d'une manière précise et rien d'autre. Auquel cas on en revient à ma première remarque : une regex ne va pas capturer une liste de trucs, point barre. Ça n'existe pas.

    Maintenant si c'est un exercice, personnellement je considérerais qu'il faut juste récupérer la String composée de tous les mots recherchés. Mais la solution de CosmoKnacki, pour autant étrange en situation réelle, répond bien mieux littéralement à l'exercice.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    Points : 71
    Points
    71
    Par défaut
    Bonsoir,

    Très intéressante cette page de test, je ne connaissais pas!

    Par contre, je n'arrive pas à la faire fonctionner!?!

    La chaîne suivante devrait être capturée comme suit (avide par défaut) dans un premier temps:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dogs are nice animals. Cats are not cat.
    Puis, les mots devraient ensuite être récupérés dans chaque groupe car ils ont tous 3 caractères et +. Dans les faits, je m'étais trompé, c'est 4 et +. Donc, on devrait y voir:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Match#1: nice
    Match#2: animals
    Match#3: Cats
    Mes autres phrases de test sont:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    My cat ate a mice. My dog once ate a fry. => Résultat: mice
    My dog once ate a fry. My cat ate a mice. => Résultat: once
    The catalog of the best dogs is around here somewhere. => No match
    Voyez-vous la phrase et la modification à la regex ({4,}) que j'ai effectuées?

    Merci!

  9. #9
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    (avide par défaut)
    je m'étais trompé, c'est 4 et +
    Ça change plutôt la donne, car dans ma première approche la pattern s'appuie sur le fait que la longueur des mots à éviter est inférieure à celle des "mots-bornes". La pattern peut certes être adaptée pour prendre cela en considération, mais ça la rendra encore plus complexe car il faut alors tester que chaque mot à éviter n'est pas un mot borne.

    Je pense que l'autre méthode est plus adaptée à ta demande de par son coté "one-shot" avec aucun traitement derrière:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?<=\b([Cc]at|[Dd]og)s?\b.{1,1000})\b\w{4,}\b(?=.*\b(?!(?i)\1s?\b)(?:[Dd]og|[Cc]at)s?\b)
    ou avec de quantificateurs parresseux (ce qui ne change pas grand chose):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (?<=\b([Cc]at|[Dd]og)s?\b.{1,1000}?)\b\w{4,}\b(?=.*?\b(?!(?i)\1s?\b)(?:[Dd]og|[Cc]at)s?\b)
    Le résultat est dans la correspondance générale (groupe 0), groupe 1 ne sert qu'à vérifier que les deux bornes sont différentes.
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    Points : 71
    Points
    71
    Par défaut
    Ça fonctionne bien! Par contre, saurais-tu valider si ma méthode ajoutée pour éliminer le mot Cats dans la première phrase te semble appropriée?

    J'ai ajouté ceci pour éliminer les mots de 4 lettres et + qui match les bornes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    (?<=\b([Cc]at|[Dd]og)s?\b.{1,1000})(?!(?:(?:\b[dDog]|[cC]at)s?\b))\b\w{4,}\b(?=.*\b(?!(?i)\1s?\b)(?:[Dd]og|[Cc]at)s?\b)
    Tu peux consulter mon test ici: http://fiddle.re/y3a91

    Dernière chose, je comprends que dans un lookahead on ne peut utiliser de .*, mais existerait-il une façon plus "propre" que {1,1000}?

    Je crois qu'on y est presque!

  11. #11
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 858
    Points : 6 556
    Points
    6 556
    Par défaut
    Oui c'est ça sauf que tu n'as pas mis le crochet fermant au bon endroit et que tu peux éviter un niveau de parenthèse, mais c'est bien l'idée. Mieux vaut aussi placer le lookahead aprés le \b, comme ça pas besoin de le mettre dans le lookahead:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    \b(?!(?:[dD]og|[cC]at)s?\b)\w{4,}\b
    Pour ce qui est d'éviter .{1,1000} dans le lookbehind (test arrière), la réponse est non, il n'y a pas d'autre méthode. À vrai dire le fait de pouvoir utiliser une expression de taille variable dans un lookbehind est une chose plutôt rare. À ma connaissance, seul le framework .net et le nouveau module regex de python le supporte complètement. Java oblige de limiter la taille des quantificateurs et de ne pas mettre d'expressions trop compliquées dedans (sinon la phase de pré-analyse de la pattern qui doit évaluer si oui ou non la taille du lookbehind est limitée perd les pédales, et à franchement parler, cette partie précise de la pré-analyse est boguée jusqu'à l'os). Les langages qui utilisent PCRE (PHP, R, la librairie boost) quant à eux peuvent seulement s'autoriser des alternatives de tailles différentes (?<=ab|abc|abcd).
    Brachygobius xanthozonus
    Ctenobrycon Gymnocorymbus

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    Points : 71
    Points
    71
    Par défaut
    Un très gros merci à toi, je n'y serais jamais arrivé sans ton aide car tu m'as permis de mieux comprendre le traitement des Regex!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Java] Problème de Regex
    Par spk4ever dans le forum Collection et Stream
    Réponses: 4
    Dernier message: 28/08/2006, 10h51
  2. Problème avec Regex
    Par trihanhcie dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 13/07/2006, 14h50
  3. problème de regex
    Par lanfeust42 dans le forum Langage
    Réponses: 11
    Dernier message: 12/07/2006, 15h32
  4. Problème avec RegEx et une Query string
    Par Erakis dans le forum Langage
    Réponses: 6
    Dernier message: 08/11/2005, 15h48
  5. Problème de Regex ... avec un point
    Par bugalood dans le forum Langage
    Réponses: 2
    Dernier message: 29/05/2005, 10h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo