2017-08-08 171 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