2

我在编程方面相当新,我正在尝试编写一个 python 程序,它将按特定列比较 2 个 .csv 文件并检查添加、删除和修改。.csv 文件均采用以下格式,包含相同数量的列,并使用 BillingNumber 作为键:

BillingNumber,CustomerName,IsActive,IsCreditHold,IsPayScan,City,State
"2","CHARLIE RYAN","Yes","No","Yes","Reading","PA"
"3","INSURANCE BILLS","","","","",""
"4","AAA","","","","",""

我只需要比较第 0、1、2 和 4 列。我尝试了许多不同的方法来实现这一点,但我没有任何运气。我知道我可以使用csv.DictReaderor将它们加载到字典中csv.reader,但之后我就卡住了。在将它们加载到内存后,我不确定从哪里开始或如何开始。

我以前试过这个:

import time
old_lines = set((line.strip() for line in open(r'Old/file1.csv', 'r+')))
file_new = open(r'New/file2.csv', 'r+')

choice = 0
choice = int( input('\nPlease choose your result format.\nEnter 1 for .txt, 2 for .csv or 3 for .json\n') )
time.sleep(1)
print(".")
time.sleep(1)
print("..")
time.sleep(1)
print("...")
time.sleep(1)
print("....")
time.sleep(1)
print('Done! Check "Different" folder for results.\n')
if choice == 1:
    file_diff = open(r'Different/diff.txt', 'w')
elif choice == 2:
    file_diff = open(r'Different/diff.csv', 'w')
elif choice == 3:
    file_diff = open(r'Different/diff.json', "w")
else: 
    print ("You MUST enter 1, 2 or 3")
    exit()

for line in file_new:
    if line.strip() not in old_lines:
        file_diff.write("** ERROR! Entry "+ line + "** Does not match previous file\n\n")
file_new.close()
file_diff.close()

它不能正常工作,因为如果有额外的行,或者缺少一行,它会将该行之后的所有内容记录为不同的。它还比较了整条线,这不是我想做的。这基本上只是一个起点,虽然它有点工作,但它对我的需要还不够具体。我真的只是在寻找一个好的起点。谢谢!

4

4 回答 4

1

我认为您使用 csv 模块走在正确的轨道上。由于 'BillingNumber' 是唯一键,我将为“旧”计费文件创建一个字典,为“新”计费文件创建另一个字典:

import csv

def make_billing_dict(csv_dict_reader):
    bdict = {}
    for entry in csv_dict_reader:
        key = entry['BillingNumber']
        bdict[key] = entry
    return bdict

with open('old.csv') as csv_file:
    old = csv.DictReader(csv_file)
    old_bills = make_billing_dict(old)

这导致以下数据结构old_bills

{'2': {'BillingNumber': '2',
       'City': 'Reading',
       'CustomerName': 'CHARLIE RYAN',
       'IsActive': 'Yes',
       'IsCreditHold': 'No',
       'IsPayScan': 'Yes',
       'State': 'PA'},
 '3': {'BillingNumber': '3',
       'City': '',
       'CustomerName': 'INSURANCE BILLS',
       'IsActive': '',
       'IsCreditHold': '',
       'IsPayScan': '',
       'State': ''},
 '4': {'BillingNumber': '4',
       'City': '',
       'CustomerName': 'AAA',
       'IsActive': '',
       'IsCreditHold': '',
       'IsPayScan': '',
       'State': ''}}

为“新”计费文件创建相同的数据结构后,您可以轻松找到差异:

# Keys that are in old_bills, but not new_bills
print set(old_bills.keys()) - set(new_bills.keys())

# Keys that are in new_bills, but not old_bills
print set(new_bills.keys()) - set(old_bills.keys())

# Compare columns for same billing records
# Will print True or False
print old_bills['2']['CustomerName'] == new_bills['2']['CustomerName']
print old_bills['2']['IsActive'] == new_bills['2']['IsActive']

显然,您不会为每个比较编写单独的打印语句。我只是在演示如何使用数据结构来发现差异。接下来,您应该编写一个函数来遍历所有可能的 BillingNumbers 并检查新旧之间的差异……但我将把这部分留给您。

于 2013-02-28T17:34:59.277 回答
0

你必须自己写这个吗?如果这是一个编程练习,所有的权力都属于你。否则,请寻找一个名为“diff”的工具,它可能以您已经可以访问的某种形式存在。它内置在许多其他工具中,例如文本编辑器,如 vim、emacs 和 notepad++,以及版本控制系统,如 subversion mercurial 和 git。

我建议您使用已建立的主力,而不是重新发明轮子。git diff是一头野兽。

于 2013-02-28T16:48:31.440 回答
0

阅读您的评论:

这只是我想弄清楚的事情。他们为工作中的新技术人员提供了一份工作清单,他们将雇用的人必须解决这个问题。

他们很可能正在寻找一些命令行功能。类似于

diff <(awk -F "\"*,\"*" '{print $1,$2,$3,$5}' csv1.csv) <(awk -F "\"*,\"*" '{print $1,$2,$3,$5}' csv2.csv)

一个可以在 bash中使用的命令,使用diff工具来比较使用 awk 选择的某些列。

这显然不是基于 python 的解决方案。但是,此解决方案确实展示了简单的基于 Unix 的工具的强大功能。

于 2013-02-28T17:13:49.817 回答
0

因为这些东西的需求有螺旋式上升的趋势,我认为将数据放入 SQLite 数据库是值得的。

由于检测行是被删除还是只是新行的逻辑可能难以实现。

在下面我假设 BillingNumber 是 id 而不会被改变。

import sqlite3
con = sqlite3.connect(":memory:")

cursor = con.cursor()
columns = "BillingNumber,CustomerName,IsActive,IsCreditHold,IsPayScan,City,State"
cursor.execute("CREATE TABLE left  (%s);" % columns)
cursor.execute("CREATE TABLE right (%s);" % columns) 

placeholders = ",".join("?" * len(columns.split(',')))

import csv
def reader(filename):
    for (lineno, line) in enumerate(open(filename)):
        if lineno > 0: # skip header
            yield line

def load_table(tablebname, filename):
    for row in csv.reader(reader(filename)):
        cursor.execute("INSERT INTO %s VALUES(%s);" % (tablebname, placeholders), row)

load_table("left",  "left.csv")
load_table("right", "right.csv")

if False:
    print "LEFT"
    for row in cursor.execute("SELECT * from left;"):
        print row[0]

        print "RIGHT"
        for row in cursor.execute("SELECT * from right;"):
            print row

def dataset(tablename, columns):
    for row in cursor.execute("SELECT * from %s;" % tablename):
        yield tuple(row[x] for x in columns)

# To use if raw data required.       
#left_dataset = dataset("left", [0,1,2,4])
#right_dataset = dataset("right", [0,1,2,4])

# COMPARE functions.
def different_rows():
    q = """SELECT left.*, right.* 
    FROM left, right
    WHERE left.BillingNumber = right.BillingNumber
    AND ( left.CustomerName !=  right.CustomerName OR 
          left.IsActive     !=  right.IsActive OR
          left.IsPayScan    !=  right.IsPayScan )
          ;
    """
    for row in cursor.execute(q):
        print "DIFFERENCE", row

def new_rows():
    q = """SELECT right.* 
    FROM right
    WHERE right.BillingNumber NOT IN ( SELECT BillingNumber FROM left)
          ;
    """
    for row in cursor.execute(q):
        print "NEW", row

different_rows()
new_rows()

OP 必须编写不同的函数来比较数据,但我总体上使用 SQL 可能更容易。

于 2013-02-28T17:35:52.403 回答