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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    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 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    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 confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    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 986
    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.

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

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    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 confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    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 986
    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".

  6. #6
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    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 986
    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.

  7. #7
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    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 confirmé
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    109
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2002
    Messages : 109
    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!

+ 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