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

Requêtes MySQL Discussion :

Comment effectuer un "negative lookahead" (?!) avec REGEXP ?


Sujet :

Requêtes MySQL

  1. #1
    Membre averti
    Homme Profil pro
    Paramétreur de progiciels
    Inscrit en
    Octobre 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Paramétreur de progiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 970
    Points : 381
    Points
    381
    Par défaut Comment effectuer un "negative lookahead" (?!) avec REGEXP ?
    Bonjour,

    Je souhaiterai utiliser l'expression régulière suivante directement dans ma requête SQL via REGEXP :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    \[(?!MOT1|MOT2|MOT3|MOT4])[^]]*]
    Cependant, le negative lookahead (?!) n'est pas permis dans celui-ci :
    #1139 - Got error 'repetition-operator operand invalid' from regexp
    Comment puis-je faire ?

    Merci,
    ZiP

  2. #2
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 346
    Points : 18 959
    Points
    18 959
    Par défaut
    Salut [zip].

    Peux-tu traduire en français ce que tu essayes de faire ?
    Donne nous un exemple parlant !

    http://dev.mysql.com/doc/refman/5.7/en/regexp.html

    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  3. #3
    Membre averti
    Homme Profil pro
    Paramétreur de progiciels
    Inscrit en
    Octobre 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Paramétreur de progiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 970
    Points : 381
    Points
    381
    Par défaut
    Bonjour,

    J'ai des données de ce type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    TEST - TEST
    TEST [MOT1] TEST - TEST
    TEST [MOT2] TEST - TEST
    TEST [MOT3] -- TEST
    TEST [MOT4] TEST
    TEST -- TEST
    TEST [MOT5] TEST - TEST
    TEST [MOT6] TEST
    TEST [MOT7] -- TEST
    Je souhaite récupérer toutes les lignes qui contiennent des "%[%]%" mais dont le contenu du crochet n'est pas MOT1 MOT2 MOT3 MOT4.

    J'espère que la demande est plus claire

    Merci d'avance pour votre aide,
    ZiP

  4. #4
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 346
    Points : 18 959
    Points
    18 959
    Par défaut
    Salut [ZiP].

    J'ai une solution !

    Il faut savoir que le regexp de MySql n'est pas celui du php.

    1) la première partie et la dernière partie, c'est-à-dire ce qui est hors des '[' et des ']', je l'ai traduit ainsi :
    --> [a-z0-9\ \-]+

    Soit :
    --> l'alphabet en minuscule : "a-z"
    --> l'alphabet en majuscule : "A-Z"
    --> les chiffres : "0-9"
    --> le blanc : "\ "
    --> le tiret : "\-"

    2) dans la partie centrale, tu désires ne pas avoir la chaîne "mot", mais par exemple la chaîne "word".

    Soit :
    --> le crochet gauche : "[[.left-square-bracket.]]"
    --> le crochet droite : "[[.right-square-bracket.]]"

    Ensuite, pour ne pas avoir une chaîne, celle-ci doit commencer par "^".
    Et pour terminer, si cette chaîne est présente une fois ou zéro fois, tu fais "()?". Le "?" signifie 0 ou 1 fois.

    Donc ne pas avoir la chaîne "MOT" mais avoir la chaîne "WORD", s'écrit : "(^MOT|WORD)?". Le "|" signifie ou.

    Après le "mot", tu désires avoir un chiffre. Tu ajoutes ceci : "[0-9]?".

    Et pour terminer, on regroupe le tout :
    --> "([[.left-square-bracket.]](^MOT|WORD)?[0-9]?[[.right-square-bracket.]])?"

    Voici ce que cela donne sur ton jeu d'essai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    --------------
    SET AUTOCOMMIT = 0
    --------------
     
    --------------
    START TRANSACTION
    --------------
     
    --------------
    DROP DATABASE IF EXISTS `base`
    --------------
     
    --------------
    CREATE DATABASE `base`
            DEFAULT CHARACTER SET `latin1`
            DEFAULT COLLATE       `latin1_general_ci`
    --------------
     
    --------------
    DROP TABLE IF EXISTS `test`
    --------------
     
    --------------
    CREATE TABLE `test` (
      `id`          int       NOT NULL AUTO_INCREMENT PRIMARY KEY,
      `val`      varchar(255)     NULL DEFAULT NULL
    ) ENGINE=InnoDB
      DEFAULT CHARSET=`latin1` COLLATE=`latin1_general_ci`
      ROW_FORMAT=COMPRESSED
    --------------
     
    --------------
    insert into `test` (`val`) values
      ('TEST - TEST'),
      ('TEST [MOT1] TEST - TEST'),
      ('TEST [MOT2] TEST - TEST'),
      ('TEST [MOT3] -- TEST'),
      ('TEST [MOT4] TEST'),
      ('TEST -- TEST'),
      ('TEST [MOT5] TEST - TEST'),
      ('TEST [MOT6] TEST'),
      ('TEST [MOT7] -- TEST'),
      ('TEST - [WORD7] - TEST')
    --------------
     
    --------------
    select concat('-->',val, '<--') as val from test
    --------------
     
    +-------------------------------+
    | val                           |
    +-------------------------------+
    | -->TEST - TEST<--             |
    | -->TEST [MOT1] TEST - TEST<-- |
    | -->TEST [MOT2] TEST - TEST<-- |
    | -->TEST [MOT3] -- TEST<--     |
    | -->TEST [MOT4] TEST<--        |
    | -->TEST -- TEST<--            |
    | -->TEST [MOT5] TEST - TEST<-- |
    | -->TEST [MOT6] TEST<--        |
    | -->TEST [MOT7] -- TEST<--     |
    | -->TEST - [WORD7] - TEST<--   |
    +-------------------------------+
    --------------
    select concat('-->',val, '<--') as val from test where val regexp '^[a-zA-Z0-9\ \-]+([[.left-square-bracket.]](^MOT|WORD)?[0-9]?[[.right-square-bracket.]])?[a-zA-Z0-9\ \-]+$'
    --------------
     
    +-----------------------------+
    | val                         |
    +-----------------------------+
    | -->TEST - TEST<--           |
    | -->TEST -- TEST<--          |
    | -->TEST - [WORD7] - TEST<-- |
    +-----------------------------+
    --------------
    COMMIT
    --------------
     
    --------------
    SET AUTOCOMMIT = 1
    --------------
     
     
    Appuyez sur une touche pour continuer...
    Je ne suis pas un expert, mais cela semble fonctionner.

    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  5. #5
    Membre averti
    Homme Profil pro
    Paramétreur de progiciels
    Inscrit en
    Octobre 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Paramétreur de progiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 970
    Points : 381
    Points
    381
    Par défaut
    Bonsoir Artemus24,

    Merci pour ta réponse très complète !

    Une précision, une chaîne sans "[" et "]" ne doit pas ressortir, j'ai donc remplacé le "?" final par un "+" et j'ai également retiré le "[0-9]?" car c'était pour mon exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ([[.left-square-bracket.]](^MAT|^TAM|^ATM)?[[.right-square-bracket.]])+
    J'ai laissé juste les mots que je souhaite exclure car il peut y en avoir une grande quantité (je ne les connais pas tous).

    Cependant, avec ce code, je n'inclus pas les mots que je ne connais pas et si je mets un "|.*" en plus, il m'inclus même les exclus (je ne sais pas si je suis très clair).

    Merci pour ton aide,
    ZiP

  6. #6
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 346
    Points : 18 959
    Points
    18 959
    Par défaut
    Salut [ZiP].

    Petit correctif : sous Php, le caractère d'échappement est l'antislash "\", et bien sous MySql, c'est "\\". Il faut le doubler.
    D'où l'écriture se simplifie en mettant :
    --> [[.left-square-bracket.]] par \\[
    --> [[.right-square-bracket.]] par \\]

    Citation Envoyé par [ZiP]
    Cependant, avec ce code, je n'inclus pas les mots que je ne connais pas et si je mets un "|.*" en plus, il m'inclus même les exclus (je ne sais pas si je suis très clair).
    Tel que tu l'expliques, on ne peut pas le faire.

    Mais il existe quand même une solution. A vrai dire, il faut d'abord faire le recensement de tous ce qui va se trouve entre tes crochets.
    Par simplification, pour ce jeu d'essai, je vais considérer que tu as seulement deux cas :
    1) chaînes commençant par "mot". Je les exclues tous, sauf "mot2".
    2) chaînes commençant par "word". Je les prends tous sauf "word2".

    Le cas 1) est facile, et s'écrit : ^m.*|mot2
    On voit que la chaîne "mot2" est incluse dans "m.*". De ce fait, "mot2" est plus restrictif que "m.*".
    Et par simplification, je mets seulement : mot2
    Puisque dans la liste, je mets seulement ce que je désire voir apparaître.

    Le cas 2) est un peu plus compliqué, car on veut tous les chaînes commençant par "word" sauf celui-ci "word2".
    Alors comment faire ? Dans les deux cas, le début est le même puisque c'est "word".
    C'est après que je sélectionne ou pas le chiffre que je désire voir apparaître ou pas. Ce qui donne : word(^2|[013-9]
    Mais par simplification, cela s'écrit : word[013-9]

    Et voici l'exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    --------------
    SET AUTOCOMMIT = 0
    --------------
     
    --------------
    START TRANSACTION
    --------------
     
    --------------
    DROP DATABASE IF EXISTS `base`
    --------------
     
    --------------
    CREATE DATABASE `base`
            DEFAULT CHARACTER SET `latin1`
            DEFAULT COLLATE       `latin1_general_ci`
    --------------
     
    --------------
    DROP TABLE IF EXISTS `test`
    --------------
     
    --------------
    CREATE TABLE `test` (
      `id`          int       NOT NULL AUTO_INCREMENT PRIMARY KEY,
      `val`      varchar(255)     NULL DEFAULT NULL
    ) ENGINE=InnoDB
      DEFAULT CHARSET=`latin1` COLLATE=`latin1_general_ci`
      ROW_FORMAT=COMPRESSED
    --------------
     
    --------------
    insert into `test` (`val`) values
      ('[MOT1]'),
      ('[MOT2]'),
      ('[MOT3]'),
      ('[WORD1]'),
      ('[WORD2]'),
      ('[WORD3]')
    --------------
     
    --------------
    select concat('-->',val, '<--') as val from test
    --------------
     
    +---------------+
    | val           |
    +---------------+
    | -->[MOT1]<--  |
    | -->[MOT2]<--  |
    | -->[MOT3]<--  |
    | -->[WORD1]<-- |
    | -->[WORD2]<-- |
    | -->[WORD3]<-- |
    +---------------+
    --------------
    select concat('-->',val, '<--') as val from test where val regexp '^\\[(MOT2|WORD[013-9])\\]$'
    --------------
     
    +---------------+
    | val           |
    +---------------+
    | -->[MOT2]<--  |
    | -->[WORD1]<-- |
    | -->[WORD3]<-- |
    +---------------+
    --------------
    COMMIT
    --------------
     
    --------------
    SET AUTOCOMMIT = 1
    --------------
     
     
    Appuyez sur une touche pour continuer...
    Cette façon de procéder peut devenir vite compliquée, surtout si dans les exclusions, tu as aussi les exceptions.

    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  7. #7
    Membre averti
    Homme Profil pro
    Paramétreur de progiciels
    Inscrit en
    Octobre 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Paramétreur de progiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 970
    Points : 381
    Points
    381
    Par défaut
    Salut Artemus24,

    Je dois procéder ainsi car le nombre de mot à inclure est variable et évolutif.

    Actuellement, je fais une requête SQL puis je traite mes données via une expression régulière de type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $re = "/\\[(?!MAT|TAM|TOM|OTM|ATM])[^]]*]/";
    J'aurai préféré avoir mes données déjà filtrées via ma requête mais MySQL ne semble pas permettre une telle souplesse.

    Merci quand même pour ton aide,
    ZiP

  8. #8
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 346
    Points : 18 959
    Points
    18 959
    Par défaut
    Salut [ZiP].

    Une autre solution en mysql, serait de ne pas passer par des regex.
    Dans ton dernier exemple, tu demandes de ne pas avoir les mots suivants, entre crochets : mat, tam, tom, otm, atm

    Il suffit d'utiliser le like de cette façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    select concat('-->',val, '<--') as val from test
    where val not like '%[mat%]%'
    and   val not like '%[tam%]%'
    and   val not like '%[tom%]%'
    and   val not like '%[otm%]%'
    and   val not like '%[atm%]%'
    ;
    Ainsi dans mysql tu peux ainsi dégrossir la quantité de lignes à traiter en php.
    Et en php, tu peux utiliser ton negative lookahead, qui doit normalement fonctionner.
    Cette solution garde une certaine souplesse d'écriture, à l'inverse du regex.

    Normalement, le regex sert à l'analyse syntaxique d'une ligne, et non à faire des sélections.

    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  9. #9
    Membre averti
    Homme Profil pro
    Paramétreur de progiciels
    Inscrit en
    Octobre 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Paramétreur de progiciels

    Informations forums :
    Inscription : Octobre 2006
    Messages : 970
    Points : 381
    Points
    381
    Par défaut
    Bonsoir,

    Oui, je vais faire comme ça, c'est un peu plus optimisé.

    Merci,
    ZiP

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 12/06/2009, 00h39
  2. comment effectuer un clic dans un DBGrid avec Delphi?
    Par nesinfo dans le forum Débuter
    Réponses: 5
    Dernier message: 20/05/2009, 22h45

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