【问题标题】:Check if string exists in multiple csv files and write row to file检查多个csv文件中是否存在字符串并将行写入文件
【发布时间】:2018-03-08 12:08:05
【问题描述】:

我得到了这个项目,我想检查一个电子邮件地址是否存在于两个或多个 csv 文件中。文件的数量可能会有所不同,它们的前缀也会有所不同,但它们将始终存储在同一目录中。

我需要以下帮助

  1. 一种在两个或多个文件中查找匹配项的方法。
  2. 一次搜索整个目录
  3. 将存在匹配地址的所有行写入新文件。
  4. 指出我可以将它用于脚本的方向,我可以将它与“if”语句一起使用,并与 webb 应用程序一起使用。

我看过

extracting rows from CSV file based on specific keywords

但这需要我知道我正在寻找的电子邮件地址,而我不知道。

对于有大量时间的人,在下面的文章中,您可以找到我到目前为止“实现”的内容以及原始文件和所需输出的示例。

将被检查的原始文件示例。行数可以变化。电子邮件地址有时也可以在第 1 列以外的其他列中找到。因此也许建议使用关键字方法?这是我还没有完成的事情。

example.csv
IP ADDRESS, FIRST TIME LOGGED IN, LAST TIME LOGGED IN, USERNAME
192.168.1.1 , 2018-03-07 11:33:22, 2018-03-07 11:33:28, Federov
E-MAIL ADDRESS, FIRST TIME LOGGED IN, LAST TIME LOGGED IN, USERNAME, 
schultz@mail.com, 2018-03-07 09:33:22, 2018-03-07 11:33:28, Boris Becker

对于保存的文件和 webb 应用程序,期望的结果类似于以下内容。

Result.csv
Match
E-MAIL ADDRESS, FIRST TIME LOGGED IN, LAST TIME LOGGED IN, USERNAME
schultz@mail.com, 2018-03-07 09:33:22, 2018-03-07 11:33:28, Boris Becker
schultz@mail.com, 2017-01-07 14:56:12, 2018-01-18 18:44:03, McEnroe

这是我目前得到的:

我尝试将我的“逐步”方法放入一个字符串中。我在一个文件夹中运行了这个字符串,其中有两个具有一个匹配地址的 .csv 文件。但是我收到零,什么都没有,nada ..没有错误消息,文件中也没有任何内容。字符串如下所示:

awk '/E-MAIL/{y=1;next}y' *.csv | awk '{print $1}' FS="," | awk 'FNR==NR{arr[$1];next}$1 in arr{print $1,"match"}' > results.csv

一步一步它可以工作,但对每个文件都这样做是一项艰巨的工作。我还必须创建新文件才能使其正常工作。

awk '/E-MAIL/{y=1;next}y' file-0A.csv > /test/file-0B.csv`
awk '{print $1}' FS="," file-0B.csv > /test/file-1A.csv
awk 'FNR==NR{arr[$1];next}$1 in arr{print $1,"match"}' file-1A.csv file-1B.csv > /test/results.csv

除了荒谬乏味而且可能很愚蠢之外,这种方法或至少在它的当前状态下,只允许在两个文件之间进行匹配,添加第三个将使它看起来需要找到匹配在所有三个文件中,而不是在任何两个所需的文件中......

此外,当前方法(如果您甚至可以称其为方法)在执行匹配步骤时不允许将附加信息与电子邮件地址一起提供,因为这将匹配例如日期或时间。 .我也不知道将此输出用于“if”语句..

操作系统是具有 root 权限的 Raspian Stretch。

如果我没有包含任何重要信息、拼写错误或以错误的方式提出此问题,我深表歉意。

非常感谢任何帮助!

【问题讨论】:

  • 一些快速 cmets:awk '/E-MAIL/{y=1;next}y' 将打印第一行之后的每一行 /E-MAIL/,您永远不会将 y 重置为 0。其次,您提到电子邮件可能出现在任何列中,那么您如何使标题正确?
  • 好点,列会有所不同,但 csv 文件将始终有一个标题为“E-MAIL ADDRESS”的列
  • 其他问题:您是否要检查file0 中的电子邮件是否在file1 .. filen。或者您是否要检查file0 ... filen 是否有重复的电子邮件。最重要的是,您的示例 Result.csv 有一个清晰的标题,如果电子邮件位于另一个字段中,则该标题可能不匹配。关键是,您的要求有点不清楚。
  • 好的,很抱歉,如果不清楚我的要求。把它煮沸;无论 csv 文件名如何(filen、file0 等),检查电子邮件是否存在于多个 csv 文件中。如果发现我不止一个文件,将电子邮件写入一个新文件。最好是电子邮件所在的整行。

标签: python csv if-statement awk match


【解决方案1】:

这可以在 Python 2.x 中按如下方式完成:

from itertools import dropwhile
from collections import defaultdict
import glob    
import csv

fieldnames = ['E-MAIL ADDRESS', 'FIRST TIME LOGGED IN', 'LAST TIME LOGGED IN', 'USERNAME']
emails = defaultdict(list)

for csv_filename in glob.glob('*.csv'):
    with open(csv_filename, 'rb') as f_input:
        csv_reader = csv.DictReader(f_input, fieldnames=fieldnames, skipinitialspace=True)
        next(dropwhile(lambda x: x['E-MAIL ADDRESS'] != 'E-MAIL ADDRESS', csv_reader))

        for row in csv_reader:
            emails[row['E-MAIL ADDRESS']].append(row)


with open('output.csv', 'wb') as f_output:
    csv_writer = csv.DictWriter(f_output, fieldnames=fieldnames, extrasaction='ignore')
    csv_writer.writeheader()

    for email, rows in sorted(emails.items()):
        if len(rows) > 1:
            csv_writer.writerows(rows)

这使用glob.glob() 函数为您提供.csv 文件的列表。它将所有电子邮件地址写入output.csv,其中在找到的所有 CSV 文件中多次看到该电子邮件地址。它会跳过所有行,直到找到以E-MAIL ADDRESS 开头的行。

【讨论】:

  • 感谢您抽出宝贵时间回答。我收到以下错误:`sudo python script.py Traceback(最近一次调用最后一次):文件“script.py”,第 8 行,在 中,带有 open('output.csv', 'w', newline=' ') as f_output: TypeError: 'newline' is an invalid keyword argument for this function
  • 您使用的是 Python 2.x,我已对其进行了更新以解决此问题。
  • 太棒了!为了清楚起见,我不知道我在寻找什么具体地址。我正在寻找可能存在的任何重复项。所以我没有在脚本开头指定的 match = "e-mail address"。这会奏效吗?再一次,让你花时间!
  • 列出所有文件中的所有重复项(即发现不止一次)需要不同的逻辑,并且需要在创建输出文件之前读取所有数据。
  • 好的,感谢您到目前为止的帮助。如果你不能为我指明一个大致的方向,我会进一步看?
【解决方案2】:

获取目录中所有文件的列表:

import os
file list = os.listdir()

您需要打开列表中的所有文件并将所有电子邮件地址写入字典。像这样:

my_dict[e_mail] = my_dict.get(e_mail, 0) + 1

这将为您提供邮件地址出现频率的计数。然后,您可以将所有出现多次的地址写入您的 outfile。

【讨论】:

    【解决方案3】:

    以下awk 是一次性程序,应该可以解决问题:

     awk '# The BEGIN statement sets the field separator FS
          BEGIN{FS="[[:blank:]]*,[[:blank:]]*"}
    
          # If the word "E-MAIL" is not found, skip to the next line
          !/E-MAIL/{next}
    
          # The line contains "E-MAIL"
          { 
            # Find the column of the email
            for(col=1;col<=NF;col++) { if (match($col,"E-MAIL")) break; }
            getline;      # retrieve the next line 
            email=$col;   # set the email to the value
            p[email]++;   # count the occurance of "email"
          }
    
          # if we have more then 2 email occurances
          # print the line to f[email] and skip to the next line
          (p[email]>2) { print > f[email]; next }
    
          # if this is the first time we have email
          # store the full line in l[email]
          (p[email]==1){l[email]=$0}
    
          # if this is the second time we find email
          (p[email]==2){
             # create filename
             f[email]=email".txt";sub(/@/,"_at_",f[email]);
             # print first line to f[email]
             print l[email]>f[email];
             # print current line to f[email]
             print > f[email]
          }' *.csv
    

    每次找到“E-MAIL”这个词时,它都会搜索该词出现的字段编号,读取下一行并检索email地址。

    然后它会执行一些逻辑,它会跟踪email 发生了多少次。

    • 如果是第一次,则将行存入l[email]
    • 如果是第二行,它会创建一个类似于"foo_at_bar.com.txt" 的文件名f[email],并在该文件中打印第一行l[email] 和当前行。
    • 如果是第三次或更多次,它只会将该行打印到f[email]

    这将为您创建所有文件。

    【讨论】:

    • 这很好用!但是它缺少输出文件中的标题。我知道这只是为了解决这个问题而进行的微小调整。我也想将此标记为答案,但由于我仅限于一个,下面将收到我的分析器,因为它具有所有功能。感谢您的时间和精力!
    猜你喜欢
    • 2021-10-07
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多