web-dev-qa-db-fra.com

Python script: Comment découper la sortie en une taille de ligne limitée?

J'utilise le script python pour séparer le domaine des e-mails respectifs, puis les regrouper en fonction de leur domaine respectif. Le script suivant fonctionne pour moi:

#!/usr/bin/env python3
from operator import itemgetter
from itertools import groupby
import os
import sys

dr = sys.argv[1]


for f in os.listdir(dr):
    write = []
    file = os.path.join(dr, f)
    lines = [[l.strip(), l.split("@")[-1].strip()] for l in open(file).readlines()]
    lines.sort(key=itemgetter(1))
    for item, occurrence in groupby(lines, itemgetter(1)):
        func = [s[0] for s in list(occurrence)]
        write.append(item+","+",".join(func))
    open(os.path.join(dr, "grouped_"+f), "wt").write("\n".join(write))

J'ai utilisé: python3 script.py /path/to/input files
L’entrée que j’ai donnée était une liste de courriels et l’a été comme:

domain1.com,[email protected],[email protected]
domain2.com,[email protected],[email protected],[email protected]

Mais le problème auquel je suis confronté est dû à la limite de MongoDB. Comme MongoDB a une limite de 16 Mo de taille de document, une seule ligne dans mon fichier de sortie est considérée comme un document par MongoDB et la taille de la ligne ne doit pas dépasser 16 Mo.
Donc, ce que je veux, c’est que le résultat devrait être limité à 21 e-mails par domaine. Si le domaine contient plus d’e-mails, il doit être imprimé sur une nouvelle ligne avec les autres courriels (si le nombre d’emails dépasse 21, nouvelle ligne avec le même nom de domaine). Je cam stocker des données en double dans le mongoDB.

Donc, le résultat final devrait ressembler à ceci:

domain1.com,[email protected],[email protected],... [email protected]
domain1.com,[email protected],.....
domain2.com,[email protected],....

Le point (.) Dans l'exemple ci-dessus représente de nombreux textes que j'ai découpés pour les rendre simples à comprendre.
J'espère que cela clarifie mon problème et espère trouver une solution.

1
Jaffer Wilson

Nouvelle version

Le script que vous avez posté regroupe en effet les emails par domaine, sans limite de nombre. Ci-dessous une version qui regroupera les courriels par domaine, mais divisera la liste trouvée en morceaux arbitraires. Chaque morceau sera imprimé dans une ligne, en commençant par le domaine correspondant.

Le scénario

#!/usr/bin/env python3
from operator import itemgetter
from itertools import groupby, islice
import os
import sys

dr = sys.argv[1]
size = 3

def chunk(it, size):
    it = iter(it); return iter(lambda: Tuple(islice(it, size)), ())

for f in os.listdir(dr):
    # list the files
    with open(os.path.join(dr, "chunked_"+f), "wt") as report: 
        file = os.path.join(dr, f)
        # create a list of email addresses and domains, sort by domain
        lines = [[l.strip(), l.split("@")[-1].strip()] for l in open(file).readlines()]
        lines.sort(key=itemgetter(1))
        # group by domain, split into chunks
        for domain, occurrence in groupby(lines, itemgetter(1)):
            adr = list(chunk([s[0] for s in occurrence], size))
            # write lines to output file
            for a in adr:
                report.write(domain+","+",".join(a)+"\n")

Utiliser

  • Copiez le script dans un fichier vide, enregistrez-le sous le nom chunked_list.py
  • Dans la section head, définissez la taille du bloc:

    size = 5
    
  • Exécutez le script avec le répertoire comme argument:

    python3 /path/to/chunked_list.py /path/to/files
    

    Il créera ensuite un fichier édité de chacun des fichiers, nommé chunked_filename, avec les courriels groupés (en bloc).

Ce qu'il fait

Le script prend en entrée un répertoire avec des fichiers comme:

email1@domain1
email2@domain1
email3@domain2
email4@domain1
email5@domain1
email6@domain2
email7@domain1
email8@domain2
email9@domain1
email10@domain2
email11@domain1

De chaque fichier, il crée une copie, comme:

domain1,email1@domain1,email2@domain1,email4@domain1
domain1,email5@domain1,email7@domain1,email9@domain1
domain1,email11@domain1
domain2,email3@domain2,email6@domain2,email8@domain2
domain2,email10@domain2

(set cunksize = 3)

1
Jacob Vlijm

Pour prendre en charge des répertoires et des fichiers volumineux et arbitraires, vous pouvez tiliser os.scandir() recevoir les fichiers un par un et les traiter ligne par ligne:

#!/usr/bin/env python3
import os

def emails_with_domain(dirpath):
    for entry in os.scandir(dirpath):
        if not entry.is_file():
            continue  # skip non-files
        with open(entry.path) as file:
            for line in file:
                email = line.strip()
                if email:  # skip blank lines
                    yield email.rpartition('@')[-1], email  # domain, email

Pour grouper les adresses électroniques par domaine, pas plus de 21 courriels par ligne, vous pouvez tiliser collections.defaultdict() :

import sys
from collections import defaultdict

dirpath = sys.argv[1]
with open('grouped_emails.txt', 'w') as output_file:
    emails = defaultdict(list)  # domain -> emails
    for domain, email in emails_with_domain(dirpath):
        domain_emails = emails[domain]
        domain_emails.append(email)
        if len(domain_emails) == 21:
            print(domain, *domain_emails, sep=',', file=output_file)
            del domain_emails[:]  # clear

    for domain, domain_emails in emails.items():
        print(domain, *domain_emails, sep=',', file=output_file)

Remarque:

  • tous les courriels sont sauvegardés dans le même fichier
  • les lignes avec le même domaine ne sont pas nécessairement adjacentes

Voir Quel est le moyen le plus "pythonique" de parcourir une liste en morceaux?

1
jfs