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

Python Discussion :

Regex - capture nommé dupliquée [Python 3.X]


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations forums :
    Inscription : Mai 2010
    Messages : 72
    Par défaut Regex - capture nommé dupliquée
    Bonjour,

    J'ai un script python chargé de traiter des fichiers et récupérer différentes données, via des regex configurable.
    Je tente dans la mesure du possible de récupérer les données avec un nombre de regex minimum.
    Mais là je coince car je dois utiliser des capture nommée avec des formats différents.
    En php, à priori une option permet de le faire, pas en python et je ne trouve pas d'alternative.
    Au pire je fait deux passes, en utilisant deux regex, mais pas optimum ;-).

    Plus concrètement (avec données fictives)...

    Exemples de data à traiter :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Dans un fichier....
    12:00 ROUTEUR01 Démarrage : version 1.2 test 03
    12:01 ROUTEUR01 Test version 1.2
     
     
    Dans un autre fichier...
    12:00 ROUTEUR02 Démarrage
    15:01 ROUTEUR02 Test version 1.3
    Pour chaque routeur, je dois récupérer la version et le numéro de test si présent. Le script va écraser les infos des routeurs à chaque nouvelle entrée qui corresponds (pour précision avec des captures nommées est indispensable pour des raisons de souplesse non illustrée ici).
    Idéalement via une seule regex. Ici, je devrais avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    ROUTEUR01;1.2;03
    ROUTEUR02;1.3
    Ma regex précédente ne sortait pas les nouvelles version 1.3 : pas de problème, j'ai modifié comme cela...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    .*(?P<Routeur>ROUTEUR\d{2}).*version\sv*(?P<Valeur>\d{1,2}.\d{2})(?:.*test.*(?P<Valeur2>\d{2}))*
    Et là, j'ai découvert que la ligne indiquant la version 1.3 existait aussi avant! C'est le drame, mon script n'indique plus les valeurs test des 1.2 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    ROUTEUR01;1.2
    ROUTEUR02;1.3
    Facile, la solution logique serait de faire soit l'un soit l'autre, en une seule regex....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    .*(?P<Routeur>ROUTEUR\d{2}).*version\s(?:v(?P<Valeur>\d{1,2}.\d{2}).*test.*(?P<Valeur2>\d{2})|(?P<Valeur>1.30))
    En PHP, avec l'option /J ça marche. En Python... quoi, il n'y a pas cette option????

    ... et depuis je pleure en remaniant dans tous les sens ;-) ...

    Alors, oui, je pourrais modifier le code autour de la regex mais ce n'est pas le but et c'est démesuré.
    Oui, je peux faire deux regex. Mon script saura le traiter... en analysant chaque ligne deux fois au lieu d'une, et ça me chiffonne!

    Alors, existe-t-il une solution? Une option cachée (pas trouvée...)? Une formulation différente de ma regex (j'avoue, je ne maitrise pas certaines méthodes)?
    Ou c'est mort en Python...

    Merci pour vos lumières et votre temps .

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 738
    Par défaut
    Salut,

    Ce que j'en ai compris c'est qu'on veut avoir un groupe optionnel nommé (test 03) qui est là ou pas. Ce que je ferais comme çà:
    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
    >>> data = '''12:00 ROUTEUR01 Démarrage : version 1.2 test 03
    12:01 ROUTEUR01 Test version 1.2
    12:00 ROUTEUR02 Démarrage
    15:01 ROUTEUR02 Test version 1.3'''
    >>> lines = data.splitlines()
    >>> rg = r'.*(?P<routeur>ROUTEUR\d{2}) .*version (?P<version>\d\.\d)(?: test (?P<test>\d+))?'
    >>> for line in lines:
    ...     print ('test: /%s/' % line)
    ...     m = re.match(rg, line)
    ...     if m: print(m.group('routeur'), m.group('version'), m.group('test'))
    ...
    test: /12:00 ROUTEUR01 Démarrage : version 1.2 test 03/
    ROUTEUR01 1.2 03
    test: /12:01 ROUTEUR01 Test version 1.2/
    ROUTEUR01 1.2 None
    test: /12:00 ROUTEUR02 Démarrage/
    test: /15:01 ROUTEUR02 Test version 1.3/
    ROUTEUR02 1.3 None
    >>>
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations forums :
    Inscription : Mai 2010
    Messages : 72
    Par défaut
    Merci wiztricks mais non ;-).

    Comme précisé "Le script va écraser les infos des routeurs à chaque nouvelle entrée" (via un INSERT OR UPDATE).
    Pas besoin du code python (sauf s'il y a un paramètres qui permet d'autoriser les doublons des groupes nommés), il est en prod, optimisé et ressemble effectivement à ce que tu propose (en plus complet, l'exemple donné est simplifié).

    Ce qu'il me faudrait c'est ne récupérer que les lignes en vert dans l'ensemble ci-dessous, avec des groupes nommés pour récupérer les données en gras.
    12:00 ROUTEUR01 Démarrage : version 1.2 test 03
    12:01 ROUTEUR01 Test version 1.2
    12:00 ROUTEUR02 Démarrage
    15:01 ROUTEUR02 Test version 1.3

    En 2 regex, je sais faire. En un regex sous PHP, je sais faire (option /J). En une regex sous Python... je ne sais pas faire .

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 738
    Par défaut
    Citation Envoyé par Shampra Voir le message
    En 2 regex, je sais faire. En un regex sous PHP, je sais faire (option /J). En une regex sous Python... je ne sais pas faire .
    Dans mon code, il n y a qu'une seule regex.
    note: et comme vous faites abstraction du contexte d'utilisation....

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    salut,

    perso j'ai pas compris un broc de ce que le PO essaye d'expliquer, est-ce que la regex est appliquée sur chaque ligne ? sur l'intégralité du fichier avec un flag re.DOTALL ? avec re.match() ? avec re.search() ? le propos c'est de recouvrir les données ? de prendre la correspondance la plus longue possible ? de récupérer l'intégralité des correspondances ?

    la seule piste c'est cette option /J qui ne semble pas exister dans le module re et dont on peut lire ici et là qu'on peut obtenir plus ou moins la même chose avec les branch reset qui eux sont au moins disponibles dans le module regex
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #!/usr/bin/env python3
    import re
    reg = re.compile('(?P<Routeur>ROUTEUR\d+).*?version\s*v?(?P<Valeur>\d+\.\d+)(?:.*?test.*?(?P<Valeur2>\d+))?', re.DOTALL)
    for fichier in ['fichier1', 'fichier2']:
       with open(fichier, 'r') as f:
          z = re.search(reg, f.read())
          print ('{}: {}'.format(fichier, ';'.join(i for i in z.groups() if i)))
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ ./pouet.py
    fichier1: ROUTEUR01;1.2;03
    fichier2: ROUTEUR02;1.3

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations forums :
    Inscription : Mai 2010
    Messages : 72
    Par défaut
    Merci pour le DOTALL, ça ne me parle pas du tout alors je vais regarder de plus près.

    Il me semblait que ""Le script va écraser les infos des routeurs à chaque nouvelle entrée" était explicite pour expliquer pourquoi la solution ne convient pas.
    Surtout que la question portait idéalement uniquement sur la regex (comment contourner ce problème de champ nommé qui ne peuvent être présent qu'une fois).
    Ce n'est pas le cas... je reformule tout.

    On a des journaux remontés par des routeurs (quelques Go/jours).
    Pour des besoins d'analyse, j'ai mis en place un script qui récupère des informations dans les journaux pour les enregistrer en BDD, soit en mode "historique" (chaque information est enregistré) soit en mode "état" (cas qui nous intéresse : on n'enregistre pour chaque routeur que la dernière information récupérées).
    Une visualisation permet d'afficher les données stockées.
    Afin d'être souple, il est possible de configurer plusieurs recherches en indiquant pour chaque un titre et une regex.
    Chacune récupère le nom du routeur, l'horodate, une ou deux valeurs (nommées VALEUR et VALEUR2).


    Côté code Python, ça donne ça :
    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
     
    #.......#
     
    # On compile les différentes regex fournies  
    # 
    patterns = []
    for groupe in config.sections():
        textPattern=config[groupe]['Regex']
        if textPattern.find("Valeur2") == -1:
            textPattern=textPattern+'(?P<Valeur2>)'
        item=[config[groupe]['TypeResult'], config[groupe]['Nom'], re.compile(textPattern)]
        patterns.append(item)
     
    #........#
     
    # et on traite les fichiers
    for file in list_files:  
        try:
            for line in open(file, errors="ignore"):
                for typeTraitement, titre, pat in patterns:                
                    for match in re.finditer(pat, line):
                       count=0
                       if typeTraitement=='MAJ':
                            logger.debug("{}, {}, {}, {}, {}, {}".format(typeTraitement, titre,numRouteur,match.group('Date'),match.group('Valeur'),match.group('Valeur2')))
                            count = bdd.updateString(titre,numRouteur,match.group('Date'),match.group('Valeur'),match.group('Valeur2')) 
                             # updateString est une fonction effectuant un UPDATE d'une table, basé sur le numéro routeur et le titre de la recherche; requête utilisée :
                             # req ="UPDATE analyse SET ELEMENT1='%s', ELEMENT2='%s', DATE_ELEMENT='%s', DATE_MODIF='%s' WHERE ID_ROUTEUR=%s and TYPE_ELEMENT='%s'" % (newValue, oldValue, dateElt,time.strftime('%Y-%m-%d %H:%M:%S'), numET, titreRecherche )                                                                                                                                   
                            totalins+=1 
                       else :
                            # Traitement de type historique, hors sujet #
                       if (count==0):
                            logger.error("L'enregistrement a échoué")
        except:
            #....

    L'un des besoins est "pour chaque routeur, récupérer sa version et le cas échéant sa phase de test" pour avoir un tableau des versions et test en cours.
    Les logs à traiter ressemblent à ça pour une version 1.2 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ....
    12:00 ROUTEUR01 Démarrage : version 1.2 test 03
    12:01 ROUTEUR01 Test version 1.2
    ....
    Et depuis peu voici pour une 1.3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ....
    15:00 ROUTEUR02 Démarrage
    15:01 ROUTEUR02 Test version 1.3
    ....
    Comment via une regex récupérer ça?
    Comme le montre le code, les valeurs doivent être récupérées nominativement avec VALEUR et VALEUR2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ID_ROUTEUR   VALEUR   VALEUR2
    ROUTEUR01    1.2         03
    ROUTEUR02    1.3
    Ma regex suivante est jetée car "A subpattern name must be unique".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    .*(?P<Routeur>ROUTEUR\d{2}).*version\s(?:v(?P<Valeur>\d{1,2}.\d{2}).*test.*(?P<Valeur2>\d{2})|(?P<Valeur>1.30))
    Si vraiment ce n'est pas jouable directement via une superbe REGEX, je suis aussi intéressé si des options existent (équivalent au /J de PHP... je vais voir le re.DOTALL merci).
    Pas de refonte du code (à moins que ça n'optimise le résultat sur un grand nombre de fichiers/lignes).

    En espérant avoir été plus clair, merci.

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

Discussions similaires

  1. Regex captures et boucles
    Par HardBlues dans le forum Langage
    Réponses: 1
    Dernier message: 22/04/2014, 16h55
  2. [RegEx] [Regex] Capturer du texte pour l'interpreter
    Par LaChips60 dans le forum Langage
    Réponses: 4
    Dernier message: 31/05/2011, 14h10
  3. regex : capture d'accents - \p{L} ne marche pas
    Par Phonatacid dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 23/04/2009, 10h38
  4. Posix regex : capture d'une sous chaine
    Par flo_k dans le forum C
    Réponses: 1
    Dernier message: 10/06/2008, 18h36
  5. [regex] groupe nommé non capturante.
    Par maa dans le forum C#
    Réponses: 3
    Dernier message: 22/10/2007, 02h19

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