Forum Programmation.python [RESOLU] [NOOB INSIDE] CONCATENER DES FICHIERS

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
6
avr.
2021

Bonjour,

je cherche à faire un truc en apparence simple. J’ai une liste de fichiers (plusieurs milliers) qui portent un numéro de séquence.

Pour des raisons pratiques, je voudrais les regrouper dans des fichiers par paquet de 500. Je voudrais que le fichier résultant reprenne les numéros de séquence (borne inférieure et supérieure).

Ex:

fic1 fic2 fic3 = fic1-3
comment vous y prendriez-vous (avec du code python) ?

Merci

  • # 2/3 idées

    Posté par  . Évalué à 3.

    Je ne suis pas certain de comprendre tout le besoin, mais ton problème (et le label [NOOB INSIDE]) me fait te proposer

    • De lister les fichiers avec le module glob. Tu peux faire plus fin en combinant os.listdir et le module regex re.

    • Tu peux ordonner tes fichiers en précisant selon quelle clef tu souhaites classer (l'ordre alphabétique n'est pas nécessairement celui que tu souhaites).
      Cette même fonction peut te permettre d'extraire l'information du ton nom de fichier pour le nom final du fichier concaténé.

    • Pour itérer 500 par 500, un truc vite fait donne

      while lst:
      head = lst[:500]
      l = lst[500:]
      print( head )

      Une rapide recherche me donne aussi

      [lst[i:i + 500] for i in range(0, len(lst), 500)]

    • Je te suggère de bien vérifier qu'il n'y a pas de trous dans la numérotation de tes fichiers, sinon tu vas t'arracher les cheveux dans quelques semaines!

    • [^] # Re: 2/3 idées

      Posté par  . Évalué à 1.

      Tout d'abord, un grand merci pour ta réponse.

      Je suis en train de chercher une solution moins « complexe ».

      Je pars sur un dictionnaire listant les 2 bornes encadrant une année:

      byYear = {2012: [920,947],
      2013: [948,999],
      2014: [1000,1051],
      2015: [1052,1103],
      2016: [1104,1155],
      2017: [1156,1203],
      2018: [1204,1260],
      2019: [1261,1312],
      2020: [1313,1364],
      2021: [1365,1378]}
      Sur la base de ça, j’ai un petit bout de code qui fait ce que je veux:

      with open('twic2012.pgn', 'w') as outfile:
      for i in tqdm(range(920, 947)):
      current_file = f"twic{i:03d}.pgn"
      with open(current_file) as infile:
      outfile.write(infile.read())
      outfile.write("\n")
      Le soucis c’est que je ne veux pas maintenir ce dict dans mon code.

      Pour la petite histoire le besoin est de me faciliter la vie pour télécharger les bases de The Week In Chess (https://theweekinchess.com/twic)

      • [^] # Re: 2/3 idées

        Posté par  . Évalué à 3.

        Peut-être pourrais-tu utiliser filter pour moins te prendre la tête avec les indices et les chiffres dans tous les sens (et les fichiers manquants)? Sur des quantités acceptables, ça devrait faire le taf.

        En code non testé écrit à la volée, ça donnerait

        for year, range_ in byYear.items():
            start, end = range_
            with open('twic%s.pgn' % year, 'w') as outfile:
                for current_file in filter( lambda twic_name: start <= int(twic_name[4:-4]) <= end, my_sorted_twic_files ):
                    with open(current_file) as infile: etc.

        Mais je ne comprends pas trop comment tu ne veux pas maintenir ce dict dans ton fichier.
        Il me semble que c'est une info fondamentale, comment t'en sortir sans?
        Tu peux séparer fonction et paramètres, mais je vois difficilement comment faire plus par rapport à ton besoin.

        • [^] # Re: 2/3 idées

          Posté par  . Évalué à 1.

          Bonne idée.

          En fait, mon petit programme devrait tenir un registre avec les infos suivantes:
          - liste des fichiers téléchargés
          - constitution d’une plus grande archive (quelles séquences dans quel fichier, etc.)

          Je dois bien avouer que je réfléchis en avançant :)

          Je compte bien faire une toute petite UI en tkinter pour permettre de faciliter 2 ou 3 choses

        • [^] # Re: 2/3 idées

          Posté par  . Évalué à 1.

          >>> for year, range_ in byYear.items():
              start, end = range_
              with open('twic%s.pgn' % year, 'w') as outfile:
                  for current_file in filter( lambda twic_name: start <= int(twic_name[4:-4]) <= end, my_sorted_twic_files ):
                      with open(current_file) as infile:
                          outfile.write(infile.read())
                          outfile.write("\n")
          
          
          Traceback (most recent call last):
            File "<pyshell#32>", line 4, in <module>
              for current_file in filter( lambda twic_name: start <= int(twic_name[4:-4]) <= end, my_sorted_twic_files ):
          NameError: name 'my_sorted_twic_files' is not defined
          
          • [^] # Re: 2/3 idées

            Posté par  . Évalué à 2.

            Oui il faut que tu définisses ta liste de fichiers.

            Comme je te le disais dans mon premier commentaire, quelque chose de ce genre devrait marcher pour avoir tous tes fichiers classés dans l'ordre.

            my_sorted_twic_files = glob.glob("twic*.pgn")
            my_sorted_twic_files.sort(key=lambda f: int(f[4:-4]))
        • [^] # Re: 2/3 idées

          Posté par  . Évalué à 2. Dernière modification le 07 avril 2021 à 13:42.

          Salut,

          Peut-être pourrais-tu utiliser filter pour moins te prendre la tête avec les indices et les chiffres dans tous les sens

          C'est pas super-complexifier la chose ?

          Le dictionnaire peut être construit à la volée, sans nécessité de le maintenir "manuellement", dans une première passe qui "prépare" les listes de numéros dans l'ordre souhaité par année, puis une seconde boucle fait le boulot de concaténation. Pas besoin de filter ;)

          Matricule 23415

          • [^] # Re: 2/3 idées

            Posté par  . Évalué à 2. Dernière modification le 07 avril 2021 à 18:16.

            C'est pas super-complexifier la chose ?

            Arf, c'est un éternel débat !

            Si tu regardes bien la liste byYear, tu verras qu'on trouve tantôt 28 fichiers (cas particulier du début), tantôt 52, d'autres fois 57. Bref, on ne peut pas itérer de 52 en 52 par ex, principalement à cause du 57 de l'année 2018.

            Donc pourquoi? Peut-être y a-t-il des trous? Je ne sais pas.
            Dans ces cas là, plutôt que me battre avec des os.exists et des if de ci de là, j'aime bien lire ce qui est disponible et filtrer, généralement ça m'évite des noeuds au cerveau.

            Difficile à dire si c'est la bonne approche ici, je n'ai pas la vision sur tout le pb.

            Et plus généralement, je trouve personnellement que faire plusieurs passes et être certain qu'on a la bonne correspondance est souvent plus difficile à faire marcher.
            Quand je code, j'aime bien raisonner macro/fonctionnel (filter, map, reduce…), ensuite il ne "reste" qu'à mettre les bonnes implémentations aux bons endroits.

            Et puis ça a la mérite de présenter des pistes à un débutant python. ;)

            • [^] # Re: 2/3 idées

              Posté par  . Évalué à 3. Dernière modification le 08 avril 2021 à 06:25.

              Salut,

              Pas de problème avec un os.listdir, on ne liste que les fichiers présents, donc on peut construire le dictionnaire à la volée sans os.exists ou if, en n'imposant pas de contraintes de bornes inférieures/supérieures (dans la limite du raisonnable :p). Tout facile sans avoir recours à des méthodes avancées "compliquées".

              Et en ce qui concerne les débutants, j'éviterai justement de leur parler dès le début de notions avancées. Tu aurais aimé qu'on te parle de lamdbas dès ton premier cours d'informatique ? Mais ce n'est que mon avis. ;)

              PS : note qu'avec ton glob plus haut, tu fais exactement ça :p

              Matricule 23415

              • [^] # Re: 2/3 idées

                Posté par  . Évalué à 1.

                Ça se tient, ça se tient ;)

                Je pense aussi que dans ce contexte, il n'était pas très clair d'où nous partions, quelles étaient les contraintes, et où nous voulions aller.

                Donc différents niveaux de suppositions et propositions.

                Enfin, gageons que nous avons aidé Xavier<, c'est le principal !

  • # Difficulté ?

    Posté par  . Évalué à 2.

    Salut,

    je cherche à faire un truc en apparence simple

    Je ne vois pas où tu bloque, même s'il y a des "trous" ou s'il fallait conserver un ordre.

    Un début de code aiderait à mieux comprendre ce qui pose problème.

    Que doit retourner : fic1 fic3 fic4 fic5 fic7 fic9 fic10 ?

    Plutôt :

    • fic1-10
    • fic1,3-5,7,9-10
    • fic1-1,3-5,7-7,9-10
    • autre ?

    Matricule 23415

    • [^] # Re: Difficulté ?

      Posté par  . Évalué à 1.

      Salut,

      dans l’exemple que tu donnes, ce serait plutôt fic1-10

      • [^] # Re: Difficulté ?

        Posté par  . Évalué à 3. Dernière modification le 07 avril 2021 à 07:44.

        Salut,

        C'est le cas le plus simple.

        Vu le code proposé plus haut, le problème est (un peu) pris à l'envers si tu veux tout faire en une passe, car tu ne sauras jamais à l'avance la fin du nom de fichier, donc il faut passer par un fichier temporaire. Sinon, en deux passes sur les noms, ça se fait aussi, et vu la taille des données à stocker (une liste d'entiers), c'est kif-kif, et là, plus besoin du fichier temporaire.

        Matricule 23415

        • [^] # Re: Difficulté ?

          Posté par  . Évalué à 1.

          S'il y a vraiment une difficulté pour trouver le nom final, il lest toujours possible, si la taille des donnés le permet de tout mettre dans un gros buffer temporaire à écrire sur disque à la fin.
          Par exemple la classe io.Stringio.

          Mais une fois le "découpage" effectué, on accède facilement aux premier et dernier fichier et à leurs indices respectifs, donc j'avoue que je pense qu'il nous manque des petites briques pour bien tout comprendre.

  • # split

    Posté par  . Évalué à 3.

    apparemment l'outil "split" en ligne de commande permet de découper le contenu d'un fichier en sous ensemble plus petit, par defaut en lot de 1000

    donc si tu as ta liste de fichiers dans un seul fichier, et que tu veux en faire un liste par paquet de 500

    split -n 500 fichier_complet masortie

    te donnera masortie_01 masortie_02… avec 500 lignes de fichier_complet dedans

    http://www.linux-france.org/article/man-fr/man1/split-1.html

    • [^] # Re: split

      Posté par  . Évalué à 0.

      Cette solution est inenvisageable car il s’agit de données structurées (PGN) sauf à parser le fichier mais ça sort du scope pour le moment.

      • [^] # Re: split

        Posté par  . Évalué à 3.

        je suis parti de TA demande

        j’ai une liste de fichiers (plusieurs milliers) qui portent un numéro de séquence […] je voudrais les regrouper dans des fichiers par paquet de 500.

        je ne parles pas de couper les fichiers, mais bien de faire la liste des fichiers (dont tu disposes deja en fait) puis de prendre cette liste, de la couper en sous-liste de 500

        ensuite ce que tu fais du contenu des fichiers à partir des sous-liste est un autre débat.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.