【问题标题】:Write key to separate csv based on value in dictionary根据字典中的值写入键以分隔 csv
【发布时间】:2013-06-06 07:51:01
【问题描述】:

[使用 Python3] 我有一个 csv 文件,其中包含我想要的两列(一个电子邮件地址和一个国家代码;如果原始文件中不是这种情况,脚本实际上会使其成为两列)按第二列中的值拆分并输出到单独的 csv 文件中。

eppetj@desrfpkwpwmhdc.com       us      ==> output-us.csv
uheuyvhy@zyetccm.com            de      ==> output-de.csv
avpxhbdt@reywimmujbwm.com       es      ==> output-es.csv
gqcottyqmy@romeajpui.com        it      ==> output-it.csv
qscar@tpcptkfuaiod.com          fr      ==> output-fr.csv
qshxvlngi@oxnzjbdpvlwaem.com    gb      ==> output-gb.csv
vztybzbxqq@gahvg.com            us      ==> output-us.csv
...                             ...     ...

目前我的代码类型是这样做的,但不是将每个电子邮件地址写入 csv,而是覆盖之前放置的电子邮件。有人可以帮我解决这个问题吗?

我对编程和 Python 非常陌生,我可能没有以最 Python 的方式编写代码,所以我非常感谢任何关于代码的一般反馈!

提前致谢!

代码:

import csv

def tsv_to_dict(filename):
    """Creates a reader of a specified .tsv file."""
    with open(filename, 'r') as f:
        reader = csv.reader(f, delimiter='\t') # '\t' implies tab
        email_list = []
        # Checks each list in the reader list and removes empty elements
        for lst in reader:
            email_list.append([elem for elem in lst if elem != '']) # List comprehension
        # Stores the list of lists as a dict
        email_dict = dict(email_list)
    return email_dict

def count_keys(dictionary):
    """Counts the number of entries in a dictionary."""
    return len(dictionary.keys())

def clean_dict(dictionary):
    """Removes all whitespace in keys from specified dictionary."""
    return { k.strip():v for k,v in dictionary.items() } # Dictionary comprehension

def split_emails(dictionary):
    """Splits out all email addresses from dictionary into output csv files by country code."""
    # Creating a list of unique country codes
    cc_list = []
    for v in dictionary.values():
        if not v in cc_list:
            cc_list.append(v)

    # Writing the email addresses to a csv based on the cc (value) in dictionary
    for key, value in dictionary.items():
        for c in cc_list:
            if c == value:
                with open('output-' +str(c) +'.csv', 'w') as f_out:
                    writer = csv.writer(f_out, lineterminator='\r\n')
                    writer.writerow([key])

【问题讨论】:

    标签: python dictionary python-3.x


    【解决方案1】:

    您可以使用defaultdict 大大简化此操作:

    import csv
    from collections import defaultdict
    
    emails = defaultdict(list)
    
    with open('email.tsv','r') as f:
       reader = csv.reader(f, delimiter='\t')
       for row in reader:
          if row:
             if '@' in row[0]:
               emails[row[1].strip()].append(row[0].strip()+'\n')
    
    for key,values in emails.items():
       with open('output-{}.csv'.format(key), 'w') as f:
           f.writelines(values)
    

    由于您的分隔文件不是逗号分隔,而是单列 - 您不需要 csv 模块,只需编写行即可。

    emails 字典包含每个国家/地区代码的键和所有匹配电子邮件地址的列表。为了确保正确打印电子邮件地址,我们删除所有空格并添加换行符(这样我们以后可以使用writelines)。

    一旦字典被填充,它只是一步一步通过键来创建文件,然后写出结果列表。

    【讨论】:

    • 谢谢 Burhan,不过你的代码对我来说不太适用。我收到错误IndexError: list index out of range。附言我在使用 Python3 时从 'rb' 中删除了 'b'
    • 嗯,您的电子邮件文件中可能有空行。我已经更新了答案。
    • 您的代码的输出是它为每个电子邮件地址创建了一个 csv,并将该“电子邮件地址,值”放在 csv 名称中。虽然我认为你的解决方案是更好的做法(保持简短和简单),但 martineau 的回答也完全符合我的意图,让我更加灵活并使用其他功能。
    • 好的,我想出了如何应用/修复您的代码以适用于我的文件,现在它就像一个魅力!鉴于这个解决方案更短更简单,我会选择这个作为正确答案。谢谢布尔汉。
    • @Matthijs:不用嵌套的if 语句,只需使用if row and '@' in row[0]:
    【解决方案2】:

    您的代码的问题在于,每次向其中写入条目时,它都会打开同一个国家/地区输出文件,从而覆盖可能已经存在的任何内容。

    避免这种情况的一种简单方法是一次打开所有输出文件以进行写入并将它们存储在以国家代码为关键字的字典中。同样,您可以使用另一个将每个国家/地区代码与该国家/地区输出文件的csv.writerobject 相关联。

    更新:虽然我同意 Burhan 的方法可能更胜一筹,但我觉得您认为我之前的答案由于它拥有的所有 cmets 而过长 - 所以这是另一个版本基本相同的逻辑,但使用最少的 cmets 可以让您更好地辨别其合理短的真实长度(即使使用上下文管理器)。

    import csv
    from contextlib import contextmanager
    
    @contextmanager  # to manage simultaneous opening and closing of output files
    def open_country_csv_files(countries):
        csv_files = {country: open('output-'+country+'.csv', 'w') 
                       for country in countries}
        yield csv_files
        for f in csv_files.values(): f.close()
    
    with open('email.tsv', 'r') as f:
        email_dict = {row[0]: row[1] for row in csv.reader(f, delimiter='\t') if row}
    
    countries = set(email_dict.values())
    with open_country_csv_files(countries) as csv_files:
        csv_writers = {country: csv.writer(csv_files[country], lineterminator='\r\n')
                        for country in countries}
        for email_addr,country in email_dict.items():
            csv_writers[country].writerow([email_addr])
    

    【讨论】:

    • 谢谢马蒂诺!您的解决方案完全符合我的预期!
    • Matthijs:如果您认为我的回答值得,请考虑对其进行投票(如果您自己有足够的代表)。谢谢。
    • 嗨,Martineau,不幸的是我还没有足够的声誉。我当然会这样做。
    【解决方案3】:

    不是 Python 答案,但也许你可以使用这个 Bash 解决方案。

    $ while read email country
    do
      echo $email >> output-$country.csv
    done < in.csv
    

    这会从in.csv 中读取行,将它们分成emailcountry 两部分,并将@​​987654326@ 附加(&gt;&gt;)到名为output-$country.csv 的文件中。

    【讨论】:

    • 感谢这种开箱即用的想法,但它不满足对电子邮件列表执行附加操作(例如清理电子邮件列表)的要求。
    • 你需要什么样的清理细节?
    猜你喜欢
    • 2020-01-06
    • 1970-01-01
    • 2012-07-24
    • 2012-11-19
    • 1970-01-01
    • 2014-07-03
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多