在文本文件中搜索重复记录,其中副本仅由两个字段确定

时间:2010-06-07 18:09:28

标签: python

首先,Python新手;要有耐心/善良。

接下来,我每月收到一个大文本文件(想想7百万条记录)来测试重复值。这是目录信息。我得到7个字段,但我感兴趣的两个字段是供应商代码和完整的可订购部件号。为了确定记录是否重复,我压缩部件号中的所有特殊字符(除了。和#)并创建一个压缩的部件号。重复测试成为供应商代码和压缩部件号组合。这部分相当直接。目前,我只是使用2个新列(压缩部分和重复指示符)复制原始文件。如果该部分是重复的,我在最后一个字段中输入“YES”。现在这样做了,我希望能够返回(或者更好的是,同时)获得前一个记录,其中有供应商代码/压缩部件号匹配。

到目前为止,我的代码看起来像这样:

# Compress Full Part to a Compressed Part
# and Check for Duplicates on Supplier Code
# and Compressed Part combination
import sys
import re
import time
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
start=time.time()

try:
   file1 = open("C:\Accounting\May Accounting\May.txt", "r")
except IOError:
   print >> sys.stderr, "Cannot Open Read File"
   sys.exit(1)
try:
   file2 = open(file1.name[0:len(file1.name)-4] + "_" + "COMPRESSPN.txt", "a")
except IOError:
   print >> sys.stderr, "Cannot Open Write File"
   sys.exit(1)

hdrList="CIGSUPPLIER|FULL_PART|PART_STATUS|ALIAS_FLAG|ACQUISITION_FLAG|COMPRESSED_PART|DUPLICATE_INDICATOR"
file2.write(hdrList+chr(10))
lines_seen=set()
affirm="YES"

records = file1.readlines()
for record in records:
   fields = record.split(chr(124))
      if fields[0]=="CIGSupplier":
         continue                  #If incoming file has a header line, skip it
   file2.write(fields[0]+"|"),     #Supplier Code
   file2.write(fields[1]+"|"),     #Full_Part
   file2.write(fields[2]+"|"),     #Part Status
   file2.write(fields[3]+"|"),     #Alias Flag
   file2.write(re.sub("[$\r\n]", "", fields[4])+"|"),     #Acquisition Flag
   file2.write(re.sub("[^0-9a-zA-Z.#]", "", fields[1])+"|"),   #Compressed_Part
   dupechk=fields[0]+"|"+re.sub("[^0-9a-zA-Z.#]", "", fields[1])
      if dupechk not in lines_seen:
         file2.write(chr(10))
         lines_seen.add(dupechk)
      else:
         file2.write(affirm+chr(10))

print "it took", time.time() - start, "seconds."        
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
file2.close()
file1.close()

它在不到6分钟的时间内运行,所以我对这部分很满意,即使它不优雅。现在,当我得到我的结果时,我将结果导入Access并进行自联接以找到重复项。加载/查询/导出结果访问此大小的文件大约需要一个小时,因此我希望能够将匹配的重复项导出到另一个文本文件或Excel文件。

足够困惑?

感谢。

5 个答案:

答案 0 :(得分:1)

也许您可以考虑构建一个字典,将(supplier_number, compressed_part_number)元组映射到数据结构(可能是嵌套列表,或者是自定义类的实例,以提高可读性和可维护性),保存与行匹配的行的行号信息密钥元组出现在您的文件中,可能还有完整的记录本身。

这最终会将文件中的所有数据放入一个大型的内存中字典中,根据您的要求,这可能是也可能不是问题;如果你跳过实际记录而只保留行号,那么字典就会小得多。

然后,您可以迭代遍历字典中的条目,将重复内容吐出到文件中。

答案 1 :(得分:0)

我认为您应该首先对输入文件中的条目进行排序。也许它会占用太多内存,但你应该首先尝试读取内存中的所有输入,根据dupechk的值对其进行排序,然后你可以遍历所有条目并轻松查看是否有两个或更多相同的内容记录。由于相同的记录被分组,因此很容易输出那些记录。

答案 2 :(得分:0)

对于您正在处理的大型文件,这可能更有效/可行:

  1. Sort基于供应商代码和压缩部件号的文件 - 将其转储到temporary file。我认为不值得实际修改压缩部件号,只需在需要时从完整的部件号计算。然而,这是纯粹的猜想,绝对值得一些快速的基准测试。
  2. 遍历临时文件(可能想要利用'with')。检查当前行的供应商代码和压缩的部件号是否与前一行相同 - 如果是,则表明您已找到重复。处理你认为合适的。由于文件已排序,因此减少了需要将内存中的所有行存储到一组连续相同行的内存要求。

答案 3 :(得分:0)

您已将整个文件读入内存。你不需要排序。不是集合,而是将(supplier, compressed_pn)映射到line_number_last_seen - 1。这样,当您发现重复时,您可以立即输出两个重复记录。此方法只需要传递一次文件。您不需要编写临时文件。

如果您经常使用相同的键有3个或更多记录,则可能希望使用将键映射到行索引列表的方法。在读取文件结束时,您将遍历字典以查找包含多个条目的列表。

答案 4 :(得分:0)

几点意见:

  1. 在大文件上使用file.readlines是浪费的 - 它将整个文件读入内存。相反,您应该利用文件是可迭代的,默认情况下一次读取一行。
  2. 您的文件格式基本上是CSV,管道而不是逗号作为分隔符。因此,请使用CSV模块。 CSV是用C语言编写的,可以避免大部分解释的开销。它还提供了一个很好的可迭代接口,也不需要将整个文件读入内存。
  3. 您还应该使用DictReader模块中的csv。如果标题在文件中,那么该类将解析它并进一步用作键。如果没有,请在代码中指定标题。无论哪种方式,字段[0]都没有信息且容易出错。 fields [“CIGSUPPLIER”]更加自我记录。
  4. 与阅读一样,使用csv模块进行编写。同样,您可以指定分隔符。
  5. 请勿使用file2.write(char(10))。使用file2.write('\n'),然后相应地打开您的文件。或者,如果您使用csv.writer类,则不需要这些。
  6. 否则,您的逻辑和流程看起来没问题。我总体上建议不要使用chr(*)来电,除非这个角色确实无法打印。换行符和管道是可打印的(或支持转义),应该这样使用。