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 :

Modification d'un fichier texte suivant une liste de mots


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 6
    Par défaut Modification d'un fichier texte suivant une liste de mots
    Bonjour à tous,

    Tout d'abord je découvre Python, ainsi que la programmation en général.

    Pour les besoins de mon travail, j'ai besoin, à partir d'un fichier 'initial.txt', de récuperer une partie des données de ce fichier en le modifiant suivant qqls règles pour en faire un fichier 'final.txt':

    1. Ce fichier est en .txt et est construit sous forme de lignes.
    2. Le fichier final devra garder la meme structure sauf que je veux
    pouvoir faire un tri sur chaque ligne en fonction d'une liste de mots
    3. Si un des mots de la liste apparait dans la ligne, je supprime cette ligne
    autrement, je garde cette ligne et je passe à la suivante en faisant cette
    meme opération.
    4. A partir du moment ou un des mots de cette liste se trouve dans une ligne,
    on la supprime.

    Voila mon ptit bout de code qui marche à peu près bien, sauf qu'il y a un ptit
    binz (normal, sinon je vous demenderai pas un coup d'main )

    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
    def filtre(source,destination):  #Nom de la fonction
        "recopier un fichier en éliminant les lignes de remarques"
        initial= open("C:\\TRANSFERT\\initial.txt", 'r')  #Ouverture du fichier initial
        final = open("C:\\TRANSFERT\\final.txt", 'w')  #Ouverture du fichier final
        filtre = ['Destinataire','NC','bl','Edité','i','Ce','on','lt','is','Poids'] #liste de tri
        while 1:
            txt = initial.readline() #Lecture ligne par ligne
            if txt =='':
                 break     #Test de fin de fichier 
             for i in filtre:  #Boucle defilement de la liste de tri
                 if txt.find (i)== -1:  #Test presence d'un des mots de la liste de tri
                     final.write(txt)   #On ecrit la ligne dans le nouveau fichier
     
        initial.close()  #Fermeture
        final.close()   #Fermeture
        return
     
    filtre(initial.txt', 'final.txt')  #Execution de la fonction.
    Mon petit soucis, je voudrais faire le test de presence de l'ensemble des mots qu'une seule fois par ligne et non pas un test pour chacun des mots de la liste (la boucle 'for i in filtre')

    En effet, le resultant me renvoie pour chaque ligne analysée, la copie de cette ligne autant de fois que la liste des mots a été parcourue (moins une fois si l'un des mots de la liste etait present)

    Ou alors peut etre en arretant la boucle dès qu'un des mots a été trouvé.

    Si quelqu'un voit une solution à mon petit problème, je suis preneur.
    Ou bien meme une autre facon de faire, plus j'en verrai, mieux se sera

    Merci.

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    Août 2004
    Messages
    723
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 723
    Par défaut
    Déjà, pour lire le fichier ligne par ligne, il y a moyen d'écrire ça de manière un peu plus lisible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for ligne in fichier:
        #Traiter la ligne
    Pour vérifier si une chaîne est présente dans une autre tu as le mot-clé in
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if ch1 in ch2:
        #Faire quelque chose
    D'un point de vue algorithmique, ton code lit le fichier, et pour chaque mot de ta liste, écrit la ligne courante dans le fichier de sortie si celle-ci ne contient pas le mot. Il faut d'abord tester tous les mots, et ensuite écrire, tu peux même optimiser un peu en gardant une variable booléenne (True ou False) qui te permettra de savoir si un des mots a été trouvé ou non, de cette façon tu pourras interrompre la boucle dès que l'un des mots aura été trouvé.

  3. #3
    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
    Bienvenue,

    «plus j'en verrai, mieux ce sera»
    hormise l'orthographe, j'aime bien, ça






    Quand on veut itérer sur les lignes d'un fichier, il vaut mieux utiliser un itérateur sur le fichier. Ça a l'avantage d'être léger pour la mémoire (comme l'option que tu as prise d'ailleurs) mais en plus l'itérateur se débrouille pour détecter tout seul la fin du fichier, ce qui évite les lignes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        if txt =='':
            break #Test de fin de fichier
    L'itérateur d'un fichier est un cas particulier. Habituellement un itérateur se définit d'abord. Avec un fichier il suffit d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for ligne in f:
        instructions
    et le fichier devient son propre itérateur.




    Pour ta question, j'ai pensé à la solution suivante:
    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
    def filtre(source,destination): #Nom de la fonction
    "recopier un fichier en éliminant les lignes de remarques"
     
        initial= open(source, 'r') #Ouverture du fichier initial
        final = open(destination, 'w') #Ouverture du fichier final
     
        setfiltre = set(['Destinataire','NC','bl','Edité','i','Ce','on','lt','is','Poids']) #ensemble de tri
     
        for txt in initial:  #Lecture ligne par ligne
            if not setfiltre.intersection(set(txt.split())):
                final.write(txt) #On ecrit la ligne dans le nouveau fichier
     
        initial.close() #Fermeture
        final.close() #Fermeture
     
     
    sours = "C:\\TRANSFERT\\initial.txt"
    desti = "C:\\TRANSFERT\\final.txt"
    filtre(sours,desti) #Execution de la fonction.
    Ce qui peut d'ailleurs se condenser, si je ne me trompe pas, en une 'compréhension de liste':
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [ final.write(txt) for txt in initial if not setfiltre.intersection(set(txt.split(' '))) ]
    Il faut vérifier, je n'ai pas fait tourner.



    Cependant, aussitôt fait, je pense que ta solution est meilleure:
    car setfiltre.intersection(ensemble des mots de la ligne)
    oblige le programme à passer en revue les éléments de setfiltre de façon exhaustive,
    tandis qu'une itération sur les mots de la liste filtre peut être stoppée, comme tu l'as bien vu, dès qu'un mot du filtre est trouvé dans la liste de mots de la ligne.
    De plus les instructions du genre if 'orange' in chaîne sont très rapides.
    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
    def filtre(source,destination): #Nom de la fonction
    "recopier un fichier en éliminant les lignes de remarques"
     
        initial= open(source, 'r') #Ouverture du fichier initial
        final = open(destination, 'w') #Ouverture du fichier final
     
        filtre = ['Destinataire','NC','bl','Edité','i','Ce','on','lt','is','Poids'] #liste de tri
        long = len(filtre)
     
        for txt in initial:  #Lecture ligne par ligne
            limots = txt.split()
            x = 0
            for i in filtre:
                if i in limots:
                    break
                x += 1
            if x==long:
                final.write(txt) #On ecrit la ligne dans le nouveau fichier
     
        initial.close() #Fermeture
        final.close() #Fermeture
     
     
    source = "C:\\TRANSFERT\\initial.txt"
    destination = "C:\\TRANSFERT\\final.txt"
    filtre(source,destination) #Execution de la fonction.
    Créer long évite au programme de recalculer cette valeur chaque fois qu'il doit faire le test.

    De même, il est préférable de créer linoms à chaque ligne, sinon avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
            for i in filtre:
                if i in txt.split():
                    break
                x += 1
    le calcul de txt.split() serait réeffectué à chaque i. Du moins je crois bien que c'est comme ça.




    Soit dit en passant, ton code fait rigoureusement ceci:

    - l'instruction filtre('initial.txt', 'final.txt') passe deux valeurs 'initial.txt' et 'final.txt' à la fonction filtre,
    qui les étiquette avec les noms source et destination.

    - puis la fonction crée deux objets pseudo-fichiers initial et final à partir des noms "C:\\TRANSFERT\\initial.txt" et "C:\\TRANSFERT\\final.txt" ,
    c'est à dire sans se préoccuper des valeurs 'initial.txt' et 'final.txt' désignées par les étiquettes source et destination

    Ta fonction telle qu'elle est écrite ouvrira toujours les mêmes fichiers de noms "C:\\TRANSFERT\\initial.txt" et "C:\\TRANSFERT\\final.txt" , quel que soit ce qu'on lui passera comme arguments.
    .

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    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 741
    Par défaut question naive?
    Ne serait-il pas plus judicieux de "parser" la ligne avec une expression régulière?
    -W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 77
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Par défaut
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def test (txt):
        return any(((x in txt) for x in ['un','deux','trois']))
     
    print test("un homme")
    print test("un ou deux messieurs") 
    print test("quatre ou cinq personnes")
    Ce qu'on trouve est plus important que ce qu'on cherche.
    Maths de base pour les nuls (et les autres...)

  6. #6
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 77
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Par défaut
    Citation Envoyé par eyquem Voir le message

    Ce qui peut d'ailleurs se condenser, si je ne me trompe pas, en une 'compréhension de liste':
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [ final.write(txt) for txt in initial if not setfiltre.intersection(set(txt.split(' '))) ]
    Il faut vérifier, je n'ai pas fait tourner.
    .
    Je n'ai pas vérifié non plus mais cela devrait tourner.
    'compréhension de liste' n'est pas un bon rendu pour 'comprehension list'. Les anglo-saxons agglutinent et il nous faut restituer le sens avec des conjonctions et des prépositions, ce qui n'est pas toujours facile. Ce qui existe en français standard c'est 'ensemble défini en compréhension' ce qui signifie que l'ensemble est entièrement déterminé par une propriété caractéristique de ses éléments. Cela passe aux listes 'mutatis mutandis'.
    http://gilles-dubois.developpez.com/...rehension.html
    Mais ce n'est pas le point sur lequel je veux insister ici.
    En l'occurence, plutôt qu'une 'comprehension list', il vaut mieux dans ce cas d'espèce utiliser une 'generator expression'
    http://www.python.org/dev/peps/pep-0289/
    car tu n'as pas besoin de construire la liste des appels, mais seulement d'exécuter chacun d'eux en séquence.
    Ce qu'on trouve est plus important que ce qu'on cherche.
    Maths de base pour les nuls (et les autres...)

  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
    Une expression régulière aussi, oui, j'y ai pensé.
    Mais j'ai voulu ne pas être trop long.
    Et ne pas effrayer une recrue avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    bla = '[^ ]Destinataire[$ ]|[^ ]NC[$ ]|[^ ]bl[$ ]|[^ ]Edité[$ ]'\
          '|[^ ]i[$ ]|[^ ]Ce[$ ]|[^ ]on[$ ]|[^ ]lt[$ ]|[^ ]is[$ ]|[^ ]Poids[$ ]'
    patFiltre = re.compile(bla)
    Ni le plonger dans la perplexité avec
    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
    def filtre(source,destination): #Nom de la fonction
    "recopier un fichier en éliminant les lignes de remarques"
     
        initial= open(source, 'r') #Ouverture du fichier initial
        final = open(destination, 'w') #Ouverture du fichier final
     
        filtre = ['Destinataire','NC','bl','Edité','i','Ce','on','lt','is','Poids'] #ensemble de tri
        bla = '|'.join(['[^ ]'+u+'[$ ]' for u in filtre])
        patternFiltre = re.compile(bla)
     
        for txt in initial:  #Lecture ligne par ligne
            if patternFiltre.search(txt):
                continue
            final.write(txt) #On ecrit la ligne dans le nouveau fichier
     
        initial.close() #Fermeture
        final.close() #Fermeture
     
     
    sours = "C:\\TRANSFERT\\initial.txt"
    desti = "C:\\TRANSFERT\\final.txt"
    filtre(sours,desti) #Execution de la fonction.


    Je ne sais pas comment décider de ce qui est le plus judicieux.

    Sur la base de la vitesse d'exécution ? Je ne suis pas sûr qu'une expression régulière soit plus rapide, il faudrait tester, j'en ai la flemme.

    Et puis on peut imaginer que pour des gros fichiers, il puisse se justifier de faire l'effort de développer un code qui va adapter régulièrement (par exemple toutes les 50 lignes) l'ordre des éléments dans filtre de façon à ce que les éléments qui se trouvent le plus fréquemment dans une ligne de commentaire soient les premiers de la liste filtre. De ce fait,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for i in filtre:
        if i in limots:
            break
    déclenchera plus rapidement le break en moyenne et il se pourrait que ce soit plus rapide qu'une expression régulière.





    Nota:
    ma remarque
    De plus les instructions du genre if 'orange' in chaîne sont très rapides.
    est à coté de la plaque puisqu'elle est suivie d'un code qui vérifie la présence ou non d'une chaîne dans une liste et non dans une chaîne.

    Chercher dans une liste est motivé par le fait que cette liste résulte d'un splitting de la ligne qui isole bien des mots séparés => le programme ne va donc pas détecter des fausses présences comme
    'on' in 'il est bon, le saucisson' est True


    -----------------------------


    Zavonen, bien vu de penser à any().
    Cette solution est plus élégante que mon utilisation de set.
    Elle ne présente pas non plus le défaut attaché à l'utilisation de set: any() renvoie True dès qu'elle le rencontre dans l'itérable qui lui est soumis, et ne poursuit pas son exploration de l'itérable.

    Mais ce qui rend cette solution excellente, c'est que tu fais travailler any() sur un itérable qui est un 'generator expression' puisqu'il y a des parenthèses et une itération entre les parenthèses
    return any( ( (x in txt) for x in ['un','deux','trois'] ) )
    et non pas des crochets. Donc l'itérable n'est pas construit en entier avant passage de any() , mais au fur et à mesure du besoin de any() d'un élément nouveau à examiner.

    (Je m'étends ainsi pour nicolas ser, pas pour toi, évid)





    Ce qui est marrant, c'est que tu soulignes l'intérêt des 'generator expressions' dans le post suivant et non pas à propos de cette utilisation de any() alors que c'est pour ce cas de any() qu'il est plus justifié d'en parler puisque ça marche, alors que remplacer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [ final.write(txt) for txt in initial if not setfiltre.intersection(set(txt.split(' '))) ]
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ( final.write(txt) for txt in initial if not setfiltre.intersection(set(txt.split(' '))) )
    ne marche pas !

    J'ai testé: avec une 'generator expression', le fichier reste vide, ça n'écrit pas. Tandis que ça le fait avec une 'list comprehension'.

    Comme tu vois, ta remarque a été l'aiguillon pour moi pour comprendre les 'generator expressions', ce qui n'était pas vraiment le cas jusqu'à présent.....






    C'est d'ailleurs un peu bizarre que ça marche avec une 'list comprehension' car ce n'est pas une réelle construction d'une liste d'éléments mais une répétition d'action.

    Soit dit en passant, j'ai découvert un jour qu'on peut faire exécuter plusieurs actions dans une 'list comprehension', ce qui semble impossible a priori: il suffit de mettre les actions dans un tuple.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    li = ['234','53','298','1289','983','5678','8','77','1334']
     
    f = open('nombhref.txt','w')
    g = open('nombhreg.txt','w')
    h = open('nombhreh.txt','w')
     
    [ (f.write(u+'AAA\n'),g.write(u+' 99999\n'),h.write('XXXXX '+u+'\n')) for u in li if '3' in u ]
     
    f.close()
    g.close()
    h.close()




    Soit dit en passant (bis), j'ai lu ceci

    i.e. if a function call has a single positional argument, it can
    be a generator expression without extra parentheses, but in all other
    cases you have to parenthesize it.
    http://www.xent.com/pipermail/fork/W...03/026810.html

    Ce qui fait que ton code avec any() peut se simplifier en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def test (txt):
        return any( (x in txt) for x in ['un','deux','trois'] )
    et ça marche ! Python, c'est quelque chose, quand même !



    Merci Zavo pour la correction sur l'expression erronée 'compréhension de liste'. Je voulais éviter une expression en anglais un rien pédante, c'est raté. Il est plus exact en effet de dire 'liste en compréhension'.
    Quoique je préférerais 'liste par compréhension', ça induit plus la notion dynamique de sa création à partir d'une caractéristique (ce qui est le cas quand un programme tourne) que l'idée statique de sa définition à partir de la connaissance de cette caractéristique (ce qui est plutôt le cas en mathématique).
    .

  8. #8
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 77
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Par défaut
    Citation Envoyé par eyquem
    J'ai testé: avec une 'generator expression', le fichier reste vide, ça n'écrit pas. Tandis que ça le fait avec une 'list comprehension'.
    Il faudra que je regarde cela dès que j'en aurai le temps. J'ai a priori une petite idée. Tu as déjà remarqué que les sorties ('print' et consorts) ne sont pas des instructions comme les autres. Pour des raisons d'optimisation (vidage de buffers etc..), je crois qu'elles ne sont pas synchrones. Combien de fois ai-je dû ramer pour déboguer un programme qui ne provoquait pas une sortie attendue parce qu'une erreur intervenait APRES cette instruction.
    Si c'est vraiment la cause, il se peut qu'une exception soit levée à la dernière évaluation avec un niveau de priorité plus élevé que les sorties bufferisées en cours et donc qui les annule. C'est toujours un mystère pour moi, mais je ne m'en étonne plus. Ce n'est d'ailleurs pas une spécialité de Python, C n'est pas mal non plus pour ce genre de farces et attrapes.
    Citation Envoyé par eyquem
    Ce qui fait que ton code avec any() peut se simplifier en...
    Ça c'est très bon à savoir, moi, bête et discipliné, je n'aurai pas osé enlever ces parenthèses apparemment redondantes, en me fondant sur la doc de 'any'.
    'any' est très bien parce qu'il interrompt l'exécution dès que possible, son copain 'all' n'est pas mal non plus. Je les ai découverts quand j'ai rédigé mes cours de vulgarisation sur les quantificateurs existentiels et universels au moment de les illustrer par des programmes python.
    Ce qu'on trouve est plus important que ce qu'on cherche.
    Maths de base pour les nuls (et les autres...)

Discussions similaires

  1. Réponses: 3
    Dernier message: 03/10/2011, 09h08
  2. stocker un fichier texte dans une liste
    Par dougie dans le forum C
    Réponses: 6
    Dernier message: 11/01/2011, 13h58
  3. Charger fichier texte dans une liste ?
    Par bahamut100 dans le forum C
    Réponses: 3
    Dernier message: 08/12/2009, 14h34
  4. importer fichiers texte dans une liste
    Par mcalus dans le forum Général Python
    Réponses: 7
    Dernier message: 29/01/2009, 14h17
  5. Réponses: 1
    Dernier message: 20/03/2007, 09h24

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