Сценарий Python: Как прервать вывод к ограниченному размеру строки?

Я использую сценарий Python для разделения домена из соответствующих электронных писем и затем группировки электронных писем согласно их соответствующему домену. Следующий сценарий работает на меня:

#!/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))

Я использовал: python3 script.py /path/to/input files
Вход, который я дал, был списком электронных писем и добрался как:

domain1.com,gemail1@domain1.com,email2@domain.com
domain2.com,email1@domain2.com,email2@domain2.com,email3@domain2.com

Но то, с чем стоит проблема, из-за предела MongoDB. Поскольку MongoDB имеет предел 16 МБ размера документа, и одну строку в моем выходном файле рассматривает как 1 документ MongoDB, и размер строки не должен идти вне 16 МБ.
Таким образом, то, что я хочу иметь, является результатом, должен быть ограничен 21 электронным письмом на домен и если домен имеет больше электронных писем затем, он должен быть распечатан на новой строке с остальными электронные письма (снова, если электронные письма превышают 21 затем новую строку с тем же доменным именем). Я бегунок храню дублирующиеся данные в mongoDB.

Таким образом, окончательный результат должен быть чем-то как следующее:

domain1.com,email1@domain1.com,email2@domain1.com,... email21@domain1.com
domain1.com,email22@domain1.com,.....
domain2.com,email1@domain2.com,....

Точка (.) в вышеупомянутом примере представляет многих текст, который я прервал для создания простым понять.
Надежда это разъясняет мою проблему и надеющийся получить решение для него.

1
задан 28 January 2017 в 16:26

2 ответа

Новая версия

Сценарий, который Вы отправили действительно, группирует электронные письма доменом без предела в числе. Ниже версии, которая сгруппирует электронные письма доменом, но разделит найденный список на произвольные блоки. Каждый блок будет распечатан в строку, начиная с соответствующего домена.

Сценарий

#!/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")

Использовать

  • Скопируйте сценарий в пустой файл, сохраните его как chunked_list.py
  • В главном разделе, набор размер блока:

    size = 5
    
  • Запустите скрипт с каталогом как аргумент:

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

    Это wil затем создает отредактированный файл каждого из файлов, названных chunked_filename, с (разделенными на блоки) сгруппированными электронными письмами.

Что это делает

Сценарий берет в качестве входа каталог с файлами как:

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

Из каждого файла это создает копию, как:

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

(набор cunksize = 3)

1
ответ дан 7 December 2019 в 13:37

Для поддержки произвольных больших каталогов и файлов Вы могли использовать os.scandir() получение файлов один за другим и обработка файлов линию за линией:

#!/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

На адреса электронной почты группы доменом, не больше, чем 21 электронное письмо на строку, Вы могли использовать 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)

Примечание:

  • все электронные письма сохраняются в тот же файл
  • строки с тем же доменом не обязательно смежны

Посмотрите то, Чего большая часть "pythonic" пути состоит в том, чтобы выполнить итерации по списку в блоках?

1
ответ дан 7 December 2019 в 13:37

Другие вопросы по тегам:

Похожие вопросы: