2017-08-08 79 views
0

TL; DR:你能不能可靠迭代通过一个csv对象本身,或使用(如果/不在)对它呢?CSV阅读器重复问题

所以我有一个奇怪的问题。这似乎是间歇性的,但那是因为我不知道发生了什么。我确定有一个真正的原因。

我有以下几点:

import os 
import csv 


csv_old = 'vendor_old.csv' 
csv_new = 'vendor.csv' 
csv_sftp = 'vendor_sftp.csv' 


def check_vendor_length(): 
    with open(csv_old, 'r') as t_old: 
     vendor_old = csv.reader(t_old, delimiter = ',') 

     all_vendor_old = next(vendor_old) 
     len_vendor_old = len(all_vendor_old) 

     return len_vendor_old 


def check_vendor_old(): 
    with open(csv_old, 'r') as t_old, open(csv_new, 'r') as t_new, open(csv_sftp, 'w', newline = '') as t_sftp: 
     vendor_old = csv.reader(t_old, delimiter = ',') 
     vendor_new = csv.reader(t_new, delimiter = ',') 
     vendor_sftp = csv.writer(t_sftp, delimiter = ',') 

     all_vendor_old = [] 
     row_vendor_old = next(vendor_old) 
     row_vendor_old.insert(csv_len, 'action') 
     all_vendor_old.append(row_vendor_old) 

     for row_vendor_old in vendor_old: 
      if row_vendor_old not in vendor_new: 
       all_vendor_old.append(row_vendor_old + ['remove']) 
     vendor_sftp.writerows(all_vendor_old) 


def check_new(): 
    with open(csv_old, 'r') as t_old, open(csv_new, 'r') as t_new, open(csv_sftp, 'a', newline = '') as t_sftp: 
     vendor_old = csv.reader(t_old, delimiter = ',') 
     vendor_new = csv.reader(t_new, delimiter = ',') 
     vendor_sftp = csv.writer(t_sftp, delimiter = ',') 

     all_vendor_new = [] 
     row_vendor_new = next(vendor_new) 
     row_vendor_new.insert(csv_len, 'action') 

     for row_vendor_new in vendor_new: 
      all_vendor_new.append(row_vendor_new + ['add']) 
     vendor_sftp.writerows(all_vendor_new) 

所以,第一个功能对新老CSV比较。如果旧版中的行不在新版中,则应将其标记为删除。他们被写入一个(新的)CSV,将SFTP'd给供应商。

第二个函数只取得新CSV中的所有数据并将其附加到SFTP CSV中。

发生的事情是,昨晚生成的SFTP CSV具有所有内容的重复,唯一的区别是首先是“删除”条目,然后是所有这些条目之后的相同行,但是与'添加'而不是'删除'。

但这并不总是发生。在测试中,它按预期工作。没有dups。但由于某种原因,昨晚的SFTP再一次拥有了所有的诀窍。正在处理的CSV只有大约10列,大约5100行。

奇怪的是,当我稍微削减一下,以至于我只用了大约2-300行时,我遇到了同样的问题。

还有额外的代码没有显示,但基本上,在SFTP CSV创建后,旧的CSV已被删除,并且新的CSV被重命名为旧的CSV名称。然后,第二天,当新CSV被转储到目录中时,脚本可以再次运行。冲洗并重复。

我们能确定的是,也许通过CSV对象上迭代最接近(vendor_new = csv.reader(t_new,分隔符= ''))在某种程度上borking过程。所以我做了什么修改的check_vendor_old功能,使新的CSV被读取到一个列表,然后再在for循环检查对新CSV列表旧CSV行:

def check_vendor_old(): 
    with open(csv_old, 'r') as t_old, open(csv_new, 'r') as t_new, open(csv_sftp, 'w', newline = '') as t_sftp: 
     vendor_old = csv.reader(t_old, delimiter = ',') 
     vendor_new = csv.reader(t_new, delimiter = ',') 
     vendor_sftp = csv.writer(t_sftp, delimiter = ',') 

     all_vendor_old = [] 
     row_vendor_old = next(vendor_old) 
     row_vendor_old.insert(csv_len, 'action') 
     all_vendor_old.append(row_vendor_old) 


     ################ NEW STUFF HERE ################ 
     # Create list comprised of new vendor.csv rows 
     list_vendor_new = [] 
     for row in vendor_new: 
      list_vendor_new.append(row) 
     # print(list_vendor_new) 
     ################ NEW STUFF HERE ################ 


     for row_vendor_old in vendor_old: 
      if row_vendor_old not in list_vendor_new: 
       all_vendor_old.append(row_vendor_old + ['remove']) 
     vendor_sftp.writerows(all_vendor_old) 

所以,这一切现在似乎按预期工作......但我想我们会看到。

所以我的问题是,是否有什么,我错过了当阅读csv阅读器文档?你能不能可靠遍历csv对象本身,或者使用[if/not in]来对付它?它似乎在测试过程中起作用,但显然昨晚没有按计划进行。

UPDATE
我发现vendor_new.seek(0)不工作(因为vendor_new类是_csv.reader,它没有寻求方法),但t_new(我打开CSV AS)是_io.TextIOWrapper,里面确实有寻找方法。

所以我想,如果我是这样做:

def check_vendor_old(): 
... 
    for row_vendor_old in vendor_old: 
     if row_vendor_old not in vendor_new: 
      all_vendor_old.append(row_vendor_old + ['remove']) 
     t_new.seek(0) 

这可能是更接近我想要的东西。 (如果我真的与csv.reader对象,而不是一个列表的工作。)

回答

0

你在原代码的问题是这样的:

def check_vendor_old(): 
    ... 
    for row_vendor_old in vendor_old: 
     # !!! - this is not doing what you think its doing ... 
     if row_vendor_old not in vendor_new: 
      all_vendor_old.append(row_vendor_old + ['remove']) 

现在,vendor_new是不是列表..它是一个迭代器。检查迭代器中是否有东西包含推进它直到找到匹配。下次你做这个检查时,它将从该迭代器的剩余位置开始。

为了提供一个例子,考虑这个迭代器:

>>> r = (x for x in range(10)) 

是在它5

>>> print(5 in r) 
True 

当然是了。但是3在里面吗?

>>> print(3 in r) 
False 

不......它不是,因为找到5时,我们提出的迭代器达到刚好超过5。如果您的结晶迭代器的列表,问题就会消失:

>>> r = list(x for x in range(10)) 
>>> print(5 in r) 
True 
>>> print(3 in r) 
True 
+0

谢谢你。尽管你的回答很有帮助,你能帮我澄清一下吗?假设'vendor_new'对象由5行组成,当我做'如果row_vendor_old不在vendor_new'中时,实际发生了什么?它是否通过'vendor_new'前进一次,然后不重置在vendor_old'循环中的下一个'for row_vendor_old?可以/应该使用'vendor_new.seek(0)'来重置'vendor_new'吗? – thisAaronMdev

+0

它是一个'iterator',它不是'file',它不是'list',它不是任何类型的容器。它只是一个'迭代器',**它所能做的就是向前迈进**。它不能“重置”。如果您需要继续检查其中的内容,请将其转换为适当的容器(就像在您工作的示例中一样)。 – donkopotamus

0

所以,我决定就这样做:

def check_vendor_old(): 
... 
    for row_vendor_old in vendor_old: 
     t_new.seek(0) ### Added this here 
     if row_vendor_old not in vendor_new: 
      all_vendor_old.append(row_vendor_old + ['remove']) 

重置t_new位置为0,在每个循环的开始。我认为它一开始并不奏效,因为它需要很长时间才能运行,但这只是因为它在一个CSV中重复5000行,而在另一个CSV中重复5000行,而不是像我一样原始的错误代码是第一次工作。

所以,我给的答案我的帖子到@donkopotamus为响应直接回答我最初问,我也不会一直能找到答案在他们的帮助,但我说这可能是我正在寻找的答案。

+0

这可能是一个非常低效的解决问题的方法,如果您继续追溯到文件的开头,在重新读取并重新读取和重新读取文件时,您将继续执行不必要的I/O操作。 – donkopotamus

+0

你说得对。我发现,这样做肯定需要更长的时间。大约30秒,与列表中的瞬间差不多。所以是的,使用seek似乎并不是最好的方法。感谢您的帮助和澄清。 – thisAaronMdev