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 :

Aux ptits génies du Python : Split


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2009
    Messages
    197
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 197
    Par défaut Aux ptits génies du Python : Split
    Bonjour,

    Dans mon projet Python, je souhaite importer dans mes fenêtres (qu'importe la librairie..) des menus définis dans des fichiers textes.
    Je souhaite partir d'une syntaxe de type (reprise d'un vieil ERP pour migration future) :

    SUBMENU MENU1 "Menu1"
    MENUITEM menu11 "Menu11"
    MENUITEM Prog1 "Programme1"
    MENUITEM Gesclient "Gestion des clients" "AF1" /I
    SUBMENU MENU2 "Menu2"
    MENUITEM menu21 "Menu21"
    MENUITEM menu22 "Menu22"
    MENUITEM menu23 "Menu23"
    ENDMENU


    L'idée est lorsqu'on clique sur menu "Gestion des clients" , ca lance le programme Gesclient..

    J'ai besoin d'extraire les données sous formes ['MENUITEM','Gesclient',"Gestion des clients","AF1",'/I']

    je parcours mon fichier avec readlines et split mais ca me retourne :['MENUITEM','Gesclient','"Gestion','"des"','"clients"',"AF1",'/I']

    Auriez vous des suggestions?

    MERCI

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Bonjour,



    Voici une première réponse.

    J’ai d’abord pensé à des manipulations avec find() ou partition() , mais je subodore des temps longs avec.

    Une fonction génératrice devrait aller plus vite, subodore-je toujours.
    Ça donne un code un peu long par rapport à une fonction sur une ligne, mais je ne suis pas un génie, je n’ai pas trouvé avec split(). Ou alors une astuce d’enfer en faisant agir d’abord replace() ? Mais je n’ai pas trouvé.

    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
    def f(ch):
        deb = apostrophe = -10
        for i in xrange(len(ch)):
            if ch[i]==' ':
                if apostrophe!=-9:
                    yield ch[deb:i]
                    deb = -10
            else:
                if deb==-10:
                    deb = i
                if ch[i]=='"':
                    apostrophe += 1
        yield ch[deb:]
     
     
     
    li = ['SUBMENU MENU1 "Menu1"',
          'MENUITEM menu11 "Menu11"',
          'MENUITEM Prog1 "Programme1"',
          'MENUITEM Gesclient "Gestion des clients" "AF1" /I',
          'SUBMENU MENU2 "Menu2"',
          'MENUITEM menu21 "Menu21"',
          'MENUITEM menu22 "Menu22"',
          'MENUITEM menu23 "Menu23"',
          'ENDMENU'                   ]
     
    for ligne in li:
        print list(f(ligne))

    ['SUBMENU', 'MENU1', '"Menu1"']
    ['MENUITEM', 'menu11', '"Menu11"']
    ['MENUITEM', 'Prog1', '"Programme1"']
    ['MENUITEM', 'Gesclient', '"Gestion des clients"', '"AF1"', '/I']
    ['SUBMENU', 'MENU2', '"Menu2"']
    ['MENUITEM', 'menu21', '"Menu21"']
    ['MENUITEM', 'menu22', '"Menu22"']
    ['MENUITEM', 'menu23', '"Menu23"']
    ['ENDMENU']

    Nota bene:

    ma solution sort '"Gestion des clients"' au lieu de "Gestion des clients".
    C’est facile à modifier. Que te faut-il exactement ?




    Maintenant, je vais chercher une solution avec une expression réguliére.

  3. #3
    Membre émérite
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Par défaut
    bonjour,

    plein de solutions, en voici une:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def _clean(string):
        return string.rstrip('"').lstrip('"')
    cleaned = map(_clean,['MENUITEM','Gesclient','"Gestion','"des"','"clients"',"AF1",'/I'])
    ou alors avec une lambda:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cleaned = map(lambda s:s.rstrip('"').lstrip('"'),['MENUITEM','Gesclient','"Gestion','"des"','"clients"',"AF1",'/I'])
    ou encore une liste comprehension:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    cleaned = [s.rstrip('"').lstrip('"') for s in ['MENUITEM','Gesclient','"Gestion','"des"','"clients"',"AF1",'/I']]
    ou alors un traitement direct avec les expressions régulières mais bon, c'est un peu moins direct pour des motifs aussi simples.

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    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
    li = ['SUBMENU MENU1 "Menu1"',
          'MENUITEM menu11 "Menu11"',
          'MENUITEM Prog1 "Programme1"',
          'MENUITEM Gesclient "Gestion des clients" "AF1" /I',
          'SUBMENU MENU2 "Menu2"',
          'MENUITEM menu21 "Menu21"',
          'MENUITEM menu22 "Menu22"',
          'MENUITEM menu23 "Menu23"',
          'ENDMENU'                   ]
     
    import re
     
    pat = re.compile('(?:"[^"]*")|(?:(?!")[^ ]+)')
     
    for ligne in li:
        print pat.findall(ligne)
    ['SUBMENU', 'MENU1', '"Menu1"']
    ['MENUITEM', 'menu11', '"Menu11"']
    ['MENUITEM', 'Prog1', '"Programme1"']
    ['MENUITEM', 'Gesclient', '"Gestion des clients"', '"AF1"', '/I']
    ['SUBMENU', 'MENU2', '"Menu2"']
    ['MENUITEM', 'menu21', '"Menu21"']
    ['MENUITEM', 'menu22', '"Menu22"']
    ['MENUITEM', 'menu23', '"Menu23"']
    ['ENDMENU']

    S’il y a des petites choses à corriger, dis-y.




    kango,
    ce sont de bonnes réponses, mais ce n’est pas la bonne question




    PS

    Une rapide mesure de temps donne:

    méthode avec fonction génératrice = 3,8 fois plus longue que méthode avec regex.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2009
    Messages
    197
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 197
    Par défaut
    Merci, avec ca je vais m'en sortir!

    C'est pour ca que j'aime Python, il y' a des solutions simples et rapides qui font gagner du temps, et une communauté toujours prête à aider!

    Je devrais potasser à l'occasion les expressions régulières.

    Encore Merci!

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    119
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 119
    Par défaut
    Je rajoute une solution encore plus courte et sans doute plus robuste (par exemple, permet d'ajouter \" entre les guillemets):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    import csvreader
    r=csv.reader(f,delimiter=' ',quotechar='"')
    for i in r: print i

  7. #7
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Excellente solution, Fructidor:
    temps divisé par 2 !

    Tu postes avec fruit



    Je retiens le module csv pour des traitements de texte.


    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
    li = ['SUBMENU MENU1 "Menu1"',
          'MENUITEM menu11 "Menu11"',
          'MENUITEM Prog1 "Programme1"',
          'MENUITEM Gesclient "Gestion des clients" "AF1" /I',
          'SUBMENU MENU2 "Menu2"',
          'MENUITEM menu21 "Menu21"',
          'MENUITEM menu22 "Menu22"',
          'MENUITEM menu23 "Menu23"',
          'ENDMENU'                   ]
     
    from time import clock
     
     
    def f(ch):
        deb = apostrophe = -10
        for i in xrange(len(ch)):
            if ch[i]==' ':
                if apostrophe!=-9:
                    yield ch[deb:i]
                    deb = -10
            else:
                if deb==-10:
                    deb = i
                if ch[i]=='"':
                    apostrophe += 1
        yield ch[deb:]
     
    te = clock()
    for i in xrange(1000):
        res = [ list(f(ligne)) for ligne in li]
    tf = clock()
    print tf-te,'  fonction generatrice'
    print
    print res
    print
     
     
     
    import re
    pat = re.compile('(?:"[^"]*")|(?:(?!")[^ ]+)')
     
    te = clock()
    for i in xrange(1000):
        res = [pat.findall(ligne) for ligne in li]
    tf = clock()
    print tf-te,'  expression reguliere'
    print
    print res
    print
     
     
     
     
     
    from csv import reader
     
    te = clock()
    for k in xrange(1000):
        res = list(reader(li,delimiter=' ',quotechar='"'))
    tf = clock()
    print tf-te,'  module csv'
    print
    print res
    print

    0.902776190829 fonction generatrice

    [['SUBMENU', 'MENU1', '"Menu1"'], ['MENUITEM', 'menu11', '"Menu11"'], ['MENUITEM', 'Prog1', '"Programme1"'], ['MENUITEM', 'Gesclient', '"Gestion des clients"', '"AF1"', '/I'], ['SUBMENU', 'MENU2', '"Menu2"'], ['MENUITEM', 'menu21', '"Menu21"'], ['MENUITEM', 'menu22', '"Menu22"'], ['MENUITEM', 'menu23', '"Menu23"'], ['ENDMENU']]

    0.22686849865 expression reguliere

    [['SUBMENU', 'MENU1', '"Menu1"'], ['MENUITEM', 'menu11', '"Menu11"'], ['MENUITEM', 'Prog1', '"Programme1"'], ['MENUITEM', 'Gesclient', '"Gestion des clients"', '"AF1"', '/I'], ['SUBMENU', 'MENU2', '"Menu2"'], ['MENUITEM', 'menu21', '"Menu21"'], ['MENUITEM', 'menu22', '"Menu22"'], ['MENUITEM', 'menu23', '"Menu23"'], ['ENDMENU']]

    0.113191760405 module csv

    [['SUBMENU', 'MENU1', 'Menu1'], ['MENUITEM', 'menu11', 'Menu11'], ['MENUITEM', 'Prog1', 'Programme1'], ['MENUITEM', 'Gesclient', 'Gestion des clients', 'AF1', '/I'], ['SUBMENU', 'MENU2', 'Menu2'], ['MENUITEM', 'menu21', 'Menu21'], ['MENUITEM', 'menu22', 'Menu22'], ['MENUITEM', 'menu23', 'Menu23'], ['ENDMENU']]

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2009
    Messages
    197
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 197
    Par défaut
    merci, je viens de tester avec quelques menus et ca fonctionne.

    Par contre, pour d'autres menus, il y a des tabulations à la place des espaces...
    et encore + marrant, des fois j'ai un espace (ou tab) devant le menuitem (pour une question de lisibilité sans doute).
    Donc le résultat escompté n'est pas là..

    Par exemple, j'ai :
    SUBMENU menu1 "Menu1"
    <TAB> MENUITEM <TAB> menu11 "menu11" <TAB>
    ..
    ENDMENU

    Ca complique bien plus la chose il me semble, l'idéal serait peut etre de traiter d'abord les TAB en les remplacants par un espace.

    Sinon je passe en revue mes fichiers menus mais il doit bien y avoir une solution

    merci pour vos contributions

  9. #9
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    On peut poser comme postulat que pour du traitement de texte, les regex permettent toujours d’apporter une solution.

    Si ce n’est pas avec une seule regex, c’est avec plusieurs, ou dans des petits algorithmes où elles interviennent (je pense à for m in pattern.finditer(ch) qui permet de faire des traitements à partir de chaque RegexObject m en fonction de ce qu’il a capturé), mais il doit être bien rare qu’absolument aucune regex ne puisse être utilisée.

    Le problème des regex étant qu’elles attrapent de la variabilité mais qu’il faut que cette variabilité soit contenue dans des bornes connues pour être attrapée.




    Alors si j’ai bien compris les nouveaux éléments inclus dans la variabilité visée, il faut remplacer la précédente regex pat par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pat = re.compile('"[^"]*"|(?!")[^ ]+(?= |\Z)(?<!<TAB>)')
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pat = re.compile('"[^"]*"|[^"][^ ]*(?= |\Z)(?<!<TAB>)')
    EDIT:
    grave erreur, c’est plutôt
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pat = re.compile('"[^"]*"|[^" ][^ ]*(?= |\Z)(?<!<TAB>)')
    qui convient !



    (?<!<TAB>) signifie: avant cette position, il ne doit pas y avoir <TAB>

    (?= |\Z) signifie: après cette position, il doit y avoir soit un blanc, soit la fin de la chaîne

    Si on n’indiquait pas avec (?= |\Z) que l’on doit absolument se trouver à la fin d’un mot (en étant devant un blanc ou à la fin de la chaîne), [^ ]+(?<!<TAB>) ne matcherait pas à la fin d’un mot mais le pourrait juste une position avant et on attraperait ’<TAB’. Y a qu’à faire l’essai pour le constater.



    L’utilité de |\Z n’apparaît que pour des lignes se terminant par un mot qui ne se termine pas par “ (et qui donc n’est pas attrapé par la première partie "[^"]*" )
    Il n’y en a pas dans les lignes que tu nous as présentées, mais j’ai prévu le coup
    EDIT
    Ah ben si tiens, il y a /I à la fin d’une ligne ....




    Pour ce qui est d’adapter l’utilisation de csv.reader à ces nouvelles conditions, je ne sais pas faire car je ne connais que des bribes du module csv.

  10. #10
    Membre confirmé

    Profil pro
    Inscrit en
    Février 2003
    Messages
    95
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Février 2003
    Messages : 95
    Par défaut
    Bonsoir,

    Utiliser indépendamment espace et tabulation comme séparateur, considérer les chaînes entre guillemets comme un seul élément même si elles contiennent des espaces, tout ça m'a fait penser au traitement des arguments en ligne de commande.
    Donc, pourquoi ne pas utiliser le module optparse.

    Et hop ! Le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    from optparse import OptionParser
     
    parser = OptionParser()
    print parser.parse_args('\tMENUITEM Gesclient "Gestion des clients" "AF1" /I')[1]
    Devrait donner le résultat suivant (code non testé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ['MENUITEM', 'Gesclient', 'Gestion des clients', 'AF1', '/I']
    Mathieu

  11. #11
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2009
    Messages
    197
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 197
    Par défaut
    Certes, il est vrai que je voudrais recuperer les arguments quelque soit les espaces ou tabulations , et egalement ce qu'il y a entre les guillements sans tenir compte des espaces. Ce qui rappelle il est vrai la ligne de commande.

    J'ai donc essayé la methode parse_args ci dessus et ca me retourne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    TypeError : 'str' object doesn't support item deletion

  12. #12
    Membre confirmé

    Profil pro
    Inscrit en
    Février 2003
    Messages
    95
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : Etats-Unis

    Informations forums :
    Inscription : Février 2003
    Messages : 95
    Par défaut
    Oups ...

    J'ai été trop vite ...
    parse_args attends pas une chaîne mais une liste.
    du coup faut découper la chaîne avant.
    => on en revient au même problème et parse_args ne sert à rien !

    Désolé d'avoir proposé n'importe quoi ...

    [Edit]Ca me turlupinait cette histoire de paramètres de ligne de commande.
    Alors j'ai fouillé la doc et j'ai trouvé ça :
    shlex.split(s[, comments[, posix]]) : Split the string s using shell-like syntax.
    D'où le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    import shlex
    lst = shlex.split('MENUITEM Gesclient "Gestion des clients" "AF1" /I')
    qui fonctionne correctement, même avec espaces, tabulations ...

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

Discussions similaires

  1. Accès aux libs de GoogleAppEngine depuis Python
    Par bertry dans le forum Réseau/Web
    Réponses: 3
    Dernier message: 27/01/2013, 14h47
  2. problème avec python et la fonction split
    Par bebemyouler dans le forum Général Python
    Réponses: 14
    Dernier message: 04/06/2012, 20h49
  3. vulnérabilité Python et Ruby aux attaques par timing
    Par eyquem dans le forum Général Python
    Réponses: 1
    Dernier message: 19/07/2010, 15h45
  4. Réponses: 3
    Dernier message: 19/11/2009, 11h36
  5. Accès rapide aux pixels en Python
    Par avironman dans le forum OpenCV
    Réponses: 4
    Dernier message: 26/08/2008, 17h03

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