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 :

Eliminer de multiples espaces pour n'en conserver qu'un seul


Sujet :

Python

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2008
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 8
    Par défaut Eliminer de multiples espaces pour n'en conserver qu'un seul
    Bonjour,

    Je souhaite récupérer des infos en lisant un fichier texte seulement la répétition de plusieurs espace me bloque.
    Je m'explique, voici la structure de fichier :

    name ------------- id --------- value#1 ------- value#2
    toto -------------- 0 ----------- 200 ----------- bla
    tatati ------------ 4 ------------ 50 ------------ plop
    titopipo ---------- 10 ----------- 1000 ---------- bouumm

    (les "-" représentent des espaces)
    Je voudrais obtenir la colonne value#1 dans un tableau (si meilleure idée je suis preneur) affin de pouvoir effectuer des calculs dessus.

    J'aimerai aussi arriver a organiser les données de manière plus propre (par exemple format csv) pour les re-écrire sur le disque.

    Merci !

  2. #2
    Membre Expert Avatar de pacificator
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 074
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 074
    Par défaut
    bonjour,

    tu peux utiliser les méthodes split, join et les liste en intentions pour effectuer ton traitement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> ligne = "toto      bla      200  10     bla   bla"
    >>> ligne.split(' ')
    ['toto', '', '', '', '', '', 'bla', '', '', '', '', '', '200', '', '10', '', '', '', '', 'bla', '', '', 'bla']
    >>> [v for v in _ if v]
    ['toto', 'bla', '200', '10', 'bla', 'bla']
    >>> ";".join(_)
    'toto;bla;200;10;bla;bla'
    >>> ";".join([v for v in ligne.split(' ') if v])
    'toto;bla;200;10;bla;bla'

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Août 2008
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 8
    Par défaut
    Pouuuf !!

    Je ne savais pas du tout qu'il était possible de faire des choses comme ca : [v for v in _ if v]

    J'ai du mal à comprendre exactement comment ca marche, mais je vais me documenter dessus.

    En tout cas ta solution marche niquel ! C'est exactement ce que je cherché à faire!

    un grand merci

  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
    Pour ma part, j’aime bien la démarche consistant à traiter un fichier sous la forme de ce qu’il est de façon essentielle:
    une chaîne d’un seul tenant.







    ---------------------------------------------------------------
    Pour un fichier ne contenant qu’un seul mot par colonne (= sans blanc dans un mot):

    après avoir créé un fichier-test par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    f = open('r470n.txt','w')
    chaine = '    name   id       value#1    value#2\n'+\
             '     vis  348           7cm    d=4mm  \n'+\
             '  boulon   13           9mm    laiton \n'+\
             'rondelle  56B          12mm    Grover'
    f.write(chaine)
    f.close()
    on fait tourner
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    import re
     
    f = open('r470n.txt')
    ch = f.read()
    f.close()
    li = re.findall('\n *.+? +.+? +(.+?) ',ch)         
    print li
    pour obtenir le résultat
    ['7cm', '9mm', '12mm']

    --------



    Pour faire la même chose en splittant, split() ne suffit pas car il faut bien faire quelque chose de spécifique pour extraire la troisième colonne.

    Une idée est d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    f = open('r470n.txt')
    ch = f.read()
    li = [v for v in ch.split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    f.close()
    print licol
    Mais avec le fichier ci-dessus dans lequel toutes les lignes ne commencent pas par au moins un blanc, 'laiton\nrondelle' va sortir comme un élément de la liste li, ce qui va mettre ’Grover’ comme élément 14 de la liste au lieu de ’12mm’ attendu.

    On est donc obligé de complexifier un peu en appliquant split() sur chaque ligne et non pas directement sur la chaîne ch du fichier d’un coup, ce qui allonge encore un peu le temps d’exécution (cf plus bas).:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    f = open('r470n.txt')
    li = [v for line in f for v in line.split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    f.close()
    print licol
    Mais ce code donne:
    ['7cm', '13', 'rondelle']
    parce que le newline '\n' au bout d'une ligne perturbe le saut par 4 quand il y a des blancs à la fin de la ligne.

    Il faut alors éliminer le caractère de newline ’\n’ au bout de chaque ligne en écrivant line[:-1]:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    f = open('r470n.txt')
    li = [v for line in f for v in line[:-1].split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    f.close()
    print licol
    Mais cette obligation de mettre [:-1] perturbera les choses si on cherche à extraire les mots de la dernière colonne
    puisque line[:-1] ampute la dernière ligne d’un caractère si le fichier ne se termine pas par un newline.
    D’où encore un problème à gérer.....


    On peut résoudre ce problème en empruntant une autre voie: remplacer les newlines par des blancs dans la chaîne du fichier:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    f = open('r470n.txt')
    ch = f.read().replace('\n',' ')
    li = [v for v in ch.split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    f.close()
    print licol
    Ouf.

    Enfin, bref..... quelle que soit l’option prise, il y a plus de complication et de lignes qu’avec une regex.











    -----------------------------------------------------------------
    L’utilisation d’une regex pour traiter d’un seul coup la chaîne du fichier d’un bloc permet de traiter sans difficulté supplémentaire un fichier pouvant comporter
    - plusieurs mots séparés par un seul blanc par colonne
    - des colonnes séparées par au moins deux blancs
    - un/des blanc(s) avant la première colonne, OU non


    Il suffit de mettre deux blancs successifs dans la RE.



    Après avoir créé un nouveau fichier par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    f = open('r470n.txt','w')
    chaine = 'name   id       value#1    value#2\n'+\
             'toto    0    200   bla\n'+\
             'tatati           4    50 artistes    plop\n'+\
             'titopipo  10             1000           bouumm\n'+\
             '  ototo  gargi   345 vetiitito  678      \n'\
             'Mont Blanc  4807 m    Haute Savoie       France\n'+\
             'Au mitan du jour,  Le soleil montre comme il darde,  Et le temps semble court,  Aux montres qui retardent.'
    f.write(chaine)
    f.close()
    l’exécution du code suivant (on remarque à peine la différence avec le précédent)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    import re
     
    f = open('r470n.txt')
    ch = f.read()
    f.close()
    li = re.findall('\n. *+?  +.+?  +(.+?)  ',ch)
    print 'li =',li
    donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    li = ['200', '50 artistes', '1000', '345 vetiitito', 'Haute Savoie', 'Et le temps semble court,']
    C’est un code simplissime, sans liste par compréhension ni itération par sauts de 4.




    - Par comparaison, l’autre code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    f = open('r470n.txt')
    te = clock()
    ch = f.read().replace('\n','  ')
    li = [v for v in ch.split('  ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    print licol
    donne
    ['200', '50 artistes', ' 1000', ' 345 vetiitito', 'Haute Savoie', 'Et le temps semble court,']
    c'est à dire qu’il y a un blanc en tête de ’ 1000’ et ' 345 vetiitito' parce qu’il y a un nombre impair de blancs devant ’1000’ et ’345 vetiitito' et qu’on splitte avec deux blancs.




    - Par dessus le marché, bien que les regex soient réputées (à tort) plus lentes, le code avec regex est tout aussi rapide, et même plus, en moyenne, que les deux autres:

    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
    f = open('r470n.txt','w')
    chaine = '     name   id       value#1    value#2\n'+\
             '      vis  348           7cm    d=4mm  \n'+\
             '   boulon   13           9mm    laiton \n'+\
             ' rondelle  56B          12mm    Grover'
    f.write(chaine)
    f.close()
     
    import re
    from time import clock
     
    print "ch = f.read().replace('\\n',' ') et [v for v in ch.split(' ') if v ]"
    f = open('r470n.txt')
    te = clock()
    ch = f.read().replace('\n',' ')
    li = [v for v in ch.split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    tf = clock()
    f.close()
    print licol,'  ',tf-te,'secondes\n'
     
    print '[v for line in f for v in line[:-1].split('  ') if v ]'
    f = open('r470n.txt')
    te = clock()
    li = [v for line in f for v in line[:-1].split('  ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    tf = clock()
    f.close()
    print licol,'  ',tf-te,'secondes\n'
     
     
    print 'regex'
    f = open('r470n.txt')
    te = clock()
    ch = f.read()
    li = re.findall('\n *.+? +.+? +(.+?) ',ch)
    tf = clock()
    f.close()
    print li,'  ',tf-te,'secondes'
    ch = f.read().replace('\n',' ') et [v for v in ch.split(' ') if v ]
    ['7cm', '9mm', '12mm'] 0.000355073061655 secondes

    [v for line in f for v in line[:-1].split() if v ]
    [' 7cm', ' 9mm', '12mm'] 0.000267073050054 secondes

    regex
    ['7cm', '9mm', '12mm'] 0.000245003207965 secondes
    NB : dans cette comparison des temps d’exécution , j’ai mis un blanc devant ’ rondelle’ pour que le deuxième code donne bien le bon résultat (sinon: cf plus haut).



    - Enfin, s'il faut extraire les données d’une dernière colonne, par exemple ici de la quatrième, il suffit d’introduire le drapeau MULTILINE pour rendre ’$’ apte à matcher avec les fins de lignes.

    Et de mettre un OU sous la forme ’|’ pour tenir compte qu’une ligne peut terminer par plusieurs blancs ou pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    li = re.findall('\n *.+?  +.+?  +.+?  +(.+?)(?:  |$)',ch,re.MULTILINE)
    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
    f = open('r470n.txt','w')
    chaine = 'name   id       value#1    value#2\n'\
             +'toto    0    200   bla\n'\
             +'tatati           4    50 artistes    plop\n'\
             +'titopipo  10             1000           bouumm\n'\
             +'  ototo  gargi   345 vetiitito       678   \n'\
             +'Mont Blanc  4807 m    Haute Savoie       France\n'\
             +' Au mitan du jour,  Le soleil darde,  Et le temps semble court,  Aux montres qui retardent.'
    f.write(chaine)
    f.close()
     
    import re
     
    f = open('r470n.txt')
    ch = f.read()
    f.close()
    li = re.findall('\n *.+?  +.+?  +.+?  +(.+?)(?:  |$)',ch,re.MULTILINE)
    print li
    ['bla', 'plop', 'bouumm', '678', 'France', 'Aux montres qui retardent.']


    EDIT 03/09
    corrigé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    chaine = 'name   id       value#1    value#2\n'+\
             +'toto    0    200   bla\n'+\
              etc
    en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    chaine = 'name   id       value#1    value#2\n'+\
             'toto    0    200   bla\n'+\
             etc

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Août 2008
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 8
    Par défaut
    eyquem merci pour toutes les info, ca ma bien servi surtout le début avec les regex!

  6. #6
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Juste une petite précision par rapport à la solution sans regex.

    - En lisant une ligne d'un fichier texte, on récupère une chaine de caractère qui se termine par un ou 2 caractère(s) de fin de ligne (windows:'\r\n', linux: '\n'). On retire ça avec rstrip:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ligne = "toto         0              200             bla\r\n"
    ligne = ligne.rstrip('\r\n')
    - Pour éliminer les espaces, il faut utiliser split() sans argument pour éviter qu'il subsiste des espaces à éliminer par une boucle:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ligne = ligne.split()
    print ligne
    ['toto', '0', '200', 'bla']
    on peut même récupérer la valeur 200 d'un seul coup:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    print int(ligne.rstrip('\r\n').split()[2])
    200
    Tyrtamos

  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 remarque sur split(), Tyrtamos.

    En utilisant un blanc ’ ’ comme argument de split(’ ’) , j’empêche au lecteur de réaliser que split() sans argument suit un autre algorithme qu’avec argument,
    s’il n’a pas la rigueur de s’intéresser de près à cette fonction.
    Et c’est MAL, très MAL






    Mais split() sans argument élimine non seulement TOUS les blancs,
    mais aussi les ’whitespace characters’ parmi lesquels au moins ’\t’, ’\f’, ’\r’, ’\n’, ’\v’

    Il s’en suit qu’il n’est même plus besoin de rstrip() puisque line.split() va spliter une chaîne en considérant comme séparateurs tous les caractéres de la liste ’ ’, ’\t’, \f’, ’\r’, ’\n’, ’\v’





    Autrement dit, dans mon minable petit code avec replace() , les deux lignes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ch = f.read().replace('\n',' ')
    li = [v for v in ch.split(' ') if v ]
    font le travail que fait split() quand on ne l’embête pas avec un argument,
    c’est à dire qu’elles peuvent être remplacées par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    li = f.read().split()   !!!
    D’où
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    f = open('r470n.txt')
    li = f.read().split()
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    f.close()
    print licol
    et même:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    f = open('r470n.txt')
    li = f.read().split()[6::4]            !!!!!!!!!!
    f.close()





    Finalement cette solution n’est pas plus mal que mon code avec regex. Car elle ne réclame pas d'importer un module re et de concevoir une RE, et cerise sur le gateau elle est plus rapide, en moyenne:
    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
    f = open('r470n.txt','w')
    chaine = '     name   id       value#1    value#2\n'+\
             '      vis  348           7cm    d=4mm  \n'+\
             '   boulon   13           9mm    laiton \n'+\
             ' rondelle  56B          12mm    Grover'
    f.write(chaine)
    f.close()
     
    import re
    from time import clock
     
    print "ch = f.read().replace('\\n',' ') et [v for v in ch.split(' ') if v ]"
    f = open('r470n.txt')
    te = clock()
    ch = f.read().replace('\n',' ')
    li = [v for v in ch.split(' ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    tf = clock()
    f.close()
    print licol,'  ',tf-te,'secondes\n'
     
    print '[v for line in f for v in line[:-1].split('  ') if v ]'
    f = open('r470n.txt')
    te = clock()
    li = [v for line in f for v in line[:-1].split('  ') if v ]
    licol = [ li[k] for k in xrange(6,len(li),4) ]
    tf = clock()
    f.close()
    print licol,'  ',tf-te,'secondes\n'
     
     
    print 'regex'
    f = open('r470n.txt')
    te = clock()
    ch = f.read()
    li = re.findall('\n *.+? +.+? +(.+?) ',ch)
    tf = clock()
    f.close()
    print li,'  ',tf-te,'secondes\n' 
     
     
    print 'f.read().split()[6::4]'
    f = open('r470n.txt')
    te = clock()
    li = f.read().split()[6::4]
    tf = clock()
    f.close()
    print li,'  ',tf-te,'secondes'
    ch = f.read().replace('\n',' ') et [v for v in ch.split(' ') if v ]
    ['7cm', '9mm', '12mm'] 0.000345574647326 secondes

    [v for line in f for v in line[:-1].split() if v ]
    [' 7cm', ' 9mm', '12mm'] 0.000265117493655 secondes

    regex
    ['7cm', '9mm', '12mm'] 0.000246679396696 secondes

    f.read().split()[6::4]
    ['7cm', '9mm', '12mm'] 0.000196555579921 secondes

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 04/01/2007, 12h08
  2. Multiple "input" pour un LookupDispatchAction ?
    Par petitpasdelune dans le forum Struts 1
    Réponses: 3
    Dernier message: 16/06/2006, 19h28
  3. [DEBUTANT]Supprimer les espaces pour une requete
    Par tripper.dim dans le forum Oracle
    Réponses: 4
    Dernier message: 12/10/2005, 16h04
  4. [CR] Générer des espaces pour une valeur champ
    Par newpress dans le forum SAP Crystal Reports
    Réponses: 2
    Dernier message: 17/02/2005, 17h43

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