引言
你是否曾面对两个看似相同但实则暗藏玄机的Excel文件无从下手?又是否因手工对比而痛苦不堪?上回给大家带来了Excel 文件比对神器,今天我继续带你用Python打造一个专属于你的Excel文件比对神器,拯救你的眼睛和时间。如果大家还有其他的需求,欢迎在评论区留言,免费给大家完成!!!
增加功能:
1.可以按行按列进行比对
2.可以多行多列比对
3.滚动栏直接滑到最底部
准备好了吗?Let’s dive into the code, baby!
第一步:我们的英雄登场——Tkinter和OpenPyxl
在我们开始之前,你可能会问:“为什么要用Tkinter和OpenPyxl?” Tkinter可是Python自带的GUI(图形用户界面)库,简单易用,堪称界面开发新手的福音。而OpenPyxl?它是处理Excel文件的利器,能让你在Excel世界里如鱼得水,挥斥方遒!
第二步:功能分析——让我们搞懂这个神器到底做什么
在这里,我们要对两个Excel文件进行深度对比,找出每一个隐藏在格子中的差异。不仅如此,我们还要把这些差异标记出来,让你一眼就能看穿它们的猫腻!
看代码前,先来点小剧透:
- 文件选择功能:轻松选择要对比的文件、设置输出文件夹和报告位置。
- Excel对比:逐个单元格对比,找出数值或公式的不同,甚至给你详细报告。
- 多行多列对比:不仅仅是单元格,我们还可以对比整列整行。
- 结果展示:比对结果自动显示在一个带滚动条的文本框中,再也不怕结果太长看不到!
最终的效果如下图所示:
第三步:让我们见证代码的魔力
代码可能看起来有点长,但我们会逐块分析,就像解剖一只美味的代码鸡!
import tkinter as tk
from tkinter import filedialog, messagebox
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
import os
import docx
首先,介绍我们的主角:tkinter
, filedialog
, messagebox
用来处理GUI,openpyxl
处理Excel文件,os
负责路径操作,docx
是生成报告的秘密武器。
第四步:启动我们的GUI
root = tk.Tk()
root.title("Excel文件比对工具")
这两行代码,简直就是你家的门面工程。root = tk.Tk()
就像是开启了一个空的帆布,root.title()
则是在这块帆布上写下“我是谁”。
第五步:搞定文件选择和输出路径
original_button = tk.Button(file_select_frame, text="选择原始数据文件", command=lambda: select_file('original'))
comparison_button = tk.Button(file_select_frame, text="选择比对数据文件", command=lambda: select_file('comparison'))
output_excel_button = tk.Button(file_select_frame, text="选择输出文件夹", command=lambda: select_directory('output_excel'))
output_report_button = tk.Button(file_select_frame, text="选择输出报告文件", command=lambda: select_file('output_report', save=True))
这些按钮可不是随便摆的,它们可是你的左右手,让你随意选择Excel文件,指定输出路径,一切都在掌控之中。
第六步:Excel对比大戏开演
def compare_excel_sheets(file1, file2, output_excel_dir=None, output_report=None):
wb1 = load_workbook(file1, data_only=False)
wb2 = load_workbook(file2, data_only=False)
# …省略部分代码…
wb1.save(output_file1)
wb2.save(output_file2)
终于到了高潮部分!这个函数堪比侦探,逐个检查每一个单元格的内容,找出那些不老实的数值和公式,甚至还会用黄色标记帮你圈出来!
第七步:多列多行对比——让全面性不再是奢望
def compare_multiple_rows_or_columns_in_sheet(file, sheet_name, indices1, indices2, compare_by='column'):
wb = load_workbook(file, data_only=False)
# …省略部分代码…
return "\n".join(differences)
如果说单个单元格比对是小打小闹,那多列多行比对简直就是大场面。你可以同时对比多列或者多行数据,再也不用担心遗漏某个关键点!
第八步:炫酷的结果展示
result_frame = tk.Frame(root)
scrollbar = tk.Scrollbar(result_frame)
result_text = tk.Text(result_frame, wrap='word', height=20, width=100, yscrollcommand=scrollbar.set)
scrollbar.config(command=result_text.yview)
当比对结果多到看不完?别急,滑动条来了!这个部分不仅让你轻松查看所有比对结果,还能自动滑动到最新的结果位置,再长的列表也不怕!完整代码如下:
import tkinter as tk
from tkinter import filedialog, messagebox
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
import os
import docx
def compare_excel_sheets(file1, file2, output_excel_dir=None, output_report=None):
wb1 = load_workbook(file1, data_only=False)
wb2 = load_workbook(file2, data_only=False)
differences = []
fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
output_file1 = os.path.join(output_excel_dir, f"标记_{os.path.basename(file1)}")
output_file2 = os.path.join(output_excel_dir, f"标记_{os.path.basename(file2)}")
for sheet_name in wb1.sheetnames:
if sheet_name in wb2.sheetnames:
sheet1 = wb1[sheet_name]
sheet2 = wb2[sheet_name]
max_row = max(sheet1.max_row, sheet2.max_row)
max_column = max(sheet1.max_column, sheet2.max_column)
for row in range(1, max_row + 1):
for col in range(1, max_column + 1):
cell1 = sheet1.cell(row=row, column=col)
cell2 = sheet2.cell(row=row, column=col)
val1 = cell1.value
val2 = cell2.value
formula1 = cell1.data_type == 'f'
formula2 = cell2.data_type == 'f'
if val1 != val2 or formula1 != formula2:
type1 = "公式" if formula1 else "数值"
type2 = "公式" if formula2 else "数值"
diff_msg = f"工作表 '{sheet_name}' 的第 {row} 行,第 {col} 列存在差异:\n"
diff_msg += f"原始数据: 类型='{type1}', 值='{val1}'\n"
diff_msg += f"比对数据: 类型='{type2}', 值='{val2}'\n"
if type1 != type2:
diff_msg += "(注意:一个是公式,另一个是数值)\n"
elif formula1 and formula2 and val1 != val2:
diff_msg += "(公式不同)\n"
elif not formula1 and not formula2 and val1 != val2:
diff_msg += "(数值不同)\n"
differences.append(diff_msg)
# 标记不同的单元格
cell1.fill = fill
cell2.fill = fill
else:
differences.append(f"工作表 '{sheet_name}' 在两个文件中不一致。")
# 保存标记后的Excel文件
wb1.save(output_file1)
wb2.save(output_file2)
# 生成报告并保存到Word文档
if output_report:
generate_report(differences, output_report)
return differences
def generate_report(differences, output_report):
doc = docx.Document()
doc.add_heading('Excel 文件比较报告', 0)
for diff in differences:
doc.add_paragraph(diff)
doc.save(output_report)
def specific_cell_comparisons(file1, file2, sheet_name, row, col):
wb1 = load_workbook(file1, data_only=False)
wb2 = load_workbook(file2, data_only=False)
if sheet_name in wb1.sheetnames and sheet_name in wb2.sheetnames:
sheet1 = wb1[sheet_name]
sheet2 = wb2[sheet_name]
cell1 = sheet1.cell(row=row, column=col)
cell2 = sheet2.cell(row=row, column=col)
val1 = cell1.value
val2 = cell2.value
formula1 = cell1.data_type == 'f'
formula2 = cell2.data_type == 'f'
type1 = "公式" if formula1 else "数值"
type2 = "公式" if formula2 else "数值"
result_msg = f"工作表 '{sheet_name}' 的第 {row} 行,第 {col} 列比对结果:\n"
result_msg += f"原始数据: 类型='{type1}', 值='{val1}'\n"
result_msg += f"比对数据: 类型='{type2}', 值='{val2}'\n"
if type1 != type2:
result_msg += "(注意:一个是公式,另一个是数值)\n"
elif formula1 and formula2 and val1 != val2:
result_msg += "(公式不同)\n"
elif not formula1 and not formula2 and val1 != val2:
result_msg += "(数值不同)\n"
else:
result_msg += "(两者相同)\n"
return result_msg
else:
return f"工作表 '{sheet_name}' 在两个文件中不一致。"
def compare_rows_or_columns_in_sheet(file, sheet_name, name1, name2, compare_by='column'):
wb = load_workbook(file, data_only=False)
if sheet_name in wb.sheetnames:
sheet = wb[sheet_name]
header = [cell.value for cell in sheet[1]] if compare_by == 'column' else None
try:
if compare_by == 'column':
idx1 = header.index(name1) + 1
idx2 = header.index(name2) + 1
values1 = [sheet.cell(row=row, column=idx1).value for row in range(2, sheet.max_row + 1)]
values2 = [sheet.cell(row=row, column=idx2).value for row in range(2, sheet.max_row + 1)]
else:
idx1 = int(name1)
idx2 = int(name2)
values1 = [sheet.cell(row=idx1, column=col).value for col in range(1, sheet.max_column + 1)]
values2 = [sheet.cell(row=idx2, column=col).value for col in range(1, sheet.max_column + 1)]
except ValueError:
return f"在工作表 '{sheet_name}' 中找不到 '{name1}' 或 '{name2}'。"
differences = []
for i, (val1, val2) in enumerate(zip(values1, values2), start=1 if compare_by == 'column' else 1):
if val1 != val2:
if compare_by == 'column':
diff_msg = f"工作表 '{sheet_name}' 的第 {i + 1} 行, 列 '{name1}' 和 列 '{name2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
else:
diff_msg = f"工作表 '{sheet_name}' 的第 {i} 列, 行 '{name1}' 和 行 '{name2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
differences.append(diff_msg)
if not differences:
return f"工作表 '{sheet_name}' 的'{name1}' 和 '{name2}' 完全一致。"
else:
return "\n".join(differences)
else:
return f"工作表 '{sheet_name}' 不存在。"
def compare_multiple_rows_or_columns_in_sheets(file, sheet_name, indices1, indices2, compare_by='column'):
wb = load_workbook(file, data_only=False)
if sheet_name in wb.sheetnames:
sheet = wb[sheet_name]
differences = []
try:
if compare_by == 'column':
for idx1, idx2 in zip(indices1, indices2):
col1_values = [sheet.cell(row=row, column=idx1).value for row in range(2, sheet.max_row + 1)]
col2_values = [sheet.cell(row=row, column=idx2).value for row in range(2, sheet.max_row + 1)]
for i, (val1, val2) in enumerate(zip(col1_values, col2_values), start=2):
if val1 != val2:
diff_msg = f"工作表 '{sheet_name}' 的第 {i} 行, 列 '{idx1}' 和 列 '{idx2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
differences.append(diff_msg)
else:
for idx1, idx2 in zip(indices1, indices2):
row1_values = [sheet.cell(row=idx1, column=col).value for col in range(1, sheet.max_column + 1)]
row2_values = [sheet.cell(row=idx2, column=col).value for col in range(1, sheet.max_column + 1)]
for i, (val1, val2) in enumerate(zip(row1_values, row2_values), start=1):
if val1 != val2:
diff_msg = f"工作表 '{sheet_name}' 的第 {i} 列, 行 '{idx1}' 和 行 '{idx2}' 存在差异: 值1='{val1}', 值2='{val2}'\n"
differences.append(diff_msg)
except ValueError:
return f"在工作表 '{sheet_name}' 中找不到 '{indices1}' 或 '{indices2}'。"
if not differences:
return f"工作表 '{sheet_name}' 的'{indices1}' 和 '{indices2}' 完全一致。"
else:
return "\n".join(differences)
else:
return f"工作表 '{sheet_name}' 不存在。"
def select_original_file():
global original_file_path
original_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"原始数据文件: {original_file_path}\n")
def select_comparison_file():
global comparison_file_path
comparison_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"比对数据文件: {comparison_file_path}\n")
def select_output_directory():
global output_excel_dir
output_excel_dir = filedialog.askdirectory()
result_text.insert(tk.END, f"输出文件夹: {output_excel_dir}\n")
def select_output_report_file():
global output_report_path
output_report_path = filedialog.asksaveasfilename(defaultextension=".docx", filetypes=[("Word文档", "*.docx")])
result_text.insert(tk.END, f"输出报告文件: {output_report_path}\n")
def start_comparison():
if not original_file_path or not comparison_file_path:
messagebox.showwarning("警告", "请先选择原始数据和比对数据文件。")
return
if not output_excel_dir:
messagebox.showwarning("警告", "请选择输出文件夹。")
return
differences = compare_excel_sheets(original_file_path, comparison_file_path, output_excel_dir, output_report_path)
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, "比对结果:\n")
if differences:
for difference in differences:
result_text.insert(tk.END, difference + "\n\n")
else:
result_text.insert(tk.END, "两个文件完全相同。\n")
def search_specific_cell():
if not original_file_path or not comparison_file_path:
messagebox.showwarning("警告", "请先选择原始数据和比对数据文件。")
return
sheet_name = sheet_entry.get()
row = int(row_entry.get())
col = int(col_entry.get())
result_msg = specific_cell_comparison(original_file_path, comparison_file_path, sheet_name, row, col)
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, result_msg + "\n")
def compare_uploaded_columns():
if not uploaded_file_path:
messagebox.showwarning("警告", "请先选择数据文件。")
return
sheet_name = sheet_entry_col.get()
name1 = col1_entry.get()
name2 = col2_entry.get()
compare_by = compare_option.get()
result_msg = compare_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, name1, name2, compare_by)
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, result_msg + "\n")
def select_uploaded_file():
global uploaded_file_path
uploaded_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"已选择数据文件: {uploaded_file_path}\n")
def compare_uploaded_columns():
if not uploaded_file_path:
messagebox.showwarning("警告", "请先选择数据文件。")
return
sheet_name = sheet_entry_col.get()
name1 = col1_entry.get()
name2 = col2_entry.get()
compare_by = compare_option.get()
result_msg = compare_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, name1, name2, compare_by)
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, result_msg + "\n")
# 修改后的比对按钮功能
def compare_uploaded_multiple():
if not uploaded_file_path:
messagebox.showwarning("警告", "请先选择数据文件。")
return
sheet_name = sheet_entry_col.get()
indices1 = list(map(int, col1_entry.get().split(',')))
indices2 = list(map(int, col2_entry.get().split(',')))
compare_by = compare_option.get()
result_msg = compare_multiple_rows_or_columns_in_sheet(uploaded_file_path, sheet_name, indices1, indices2, compare_by)
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, result_msg + "\n")
def select_file(file_type, save=False):
global original_file_path, comparison_file_path, output_excel_dir, output_report_path, uploaded_file_path
if file_type == 'original':
original_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"原始数据文件: {original_file_path}\n")
elif file_type == 'comparison':
comparison_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"比对数据文件: {comparison_file_path}\n")
elif file_type == 'output_report':
output_report_path = filedialog.asksaveasfilename(defaultextension=".docx", filetypes=[("Word文档", "*.docx")])
result_text.insert(tk.END, f"输出报告文件: {output_report_path}\n")
elif file_type == 'uploaded':
uploaded_file_path = filedialog.askopenfilename(filetypes=[("Excel文件", "*.xlsx *.xls")])
result_text.insert(tk.END, f"已选择数据文件: {uploaded_file_path}\n")
def select_directory(directory_type):
global output_excel_dir
if directory_type == 'output_excel':
output_excel_dir = filedialog.askdirectory()
result_text.insert(tk.END, f"输出文件夹: {output_excel_dir}\n")
# 初始化主窗口
root = tk.Tk()
root.title("Excel文件比对工具")
# 初始化全局变量
original_file_path = None
comparison_file_path = None
output_excel_dir = None
output_report_path = None
uploaded_file_path = None
# 上传文件选择部分
upload_frame = tk.LabelFrame(root, text="上传文件")
upload_frame.pack(pady=10, padx=10, fill="x")
upload_button = tk.Button(upload_frame, text="上传需要比对的数据文件", command=lambda: select_file('uploaded'))
upload_button.pack(pady=5, padx=10)
# 文件选择按钮部分
file_select_frame = tk.LabelFrame(root, text="选择文件和输出选项")
file_select_frame.pack(pady=10, padx=10, fill="x")
original_button = tk.Button(file_select_frame, text="选择原始数据文件", command=lambda: select_file('original'))
original_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
comparison_button = tk.Button(file_select_frame, text="选择比对数据文件", command=lambda: select_file('comparison'))
comparison_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
output_excel_button = tk.Button(file_select_frame, text="选择输出文件夹", command=lambda: select_directory('output_excel'))
output_excel_button.grid(row=0, column=2, padx=5, pady=5, sticky="ew")
output_report_button = tk.Button(file_select_frame, text="选择输出报告文件", command=lambda: select_file('output_report', save=True))
output_report_button.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
# 比对按钮
compare_button = tk.Button(root, text="开始比对", command=start_comparison)
compare_button.pack(pady=10)
# 查找特定单元格部分
specific_cell_frame = tk.LabelFrame(root, text="查找特定单元格")
specific_cell_frame.pack(pady=10, padx=10, fill="x")
sheet_label = tk.Label(specific_cell_frame, text="工作表名称:")
sheet_label.grid(row=0, column=0, padx=5, pady=5)
sheet_entry = tk.Entry(specific_cell_frame)
sheet_entry.grid(row=0, column=1, padx=5, pady=5)
row_label = tk.Label(specific_cell_frame, text="行号:")
row_label.grid(row=1, column=0, padx=5, pady=5)
row_entry = tk.Entry(specific_cell_frame)
row_entry.grid(row=1, column=1, padx=5, pady=5)
col_label = tk.Label(specific_cell_frame, text="列号:")
col_label.grid(row=2, column=0, padx=5, pady=5)
col_entry = tk.Entry(specific_cell_frame)
col_entry.grid(row=2, column=1, padx=5, pady=5)
search_button = tk.Button(specific_cell_frame, text="查找特定单元格", command=search_specific_cell)
search_button.grid(row=3, column=0, columnspan=2, pady=10)
# 比对同一表格中的多列或多行
compare_col_frame = tk.LabelFrame(root, text="比对表格中的多列或多行")
compare_col_frame.pack(pady=10, padx=10, fill="x")
sheet_label_col = tk.Label(compare_col_frame, text="工作表名称:")
sheet_label_col.grid(row=0, column=0, padx=5, pady=5)
sheet_entry_col = tk.Entry(compare_col_frame)
sheet_entry_col.grid(row=0, column=1, padx=5, pady=5)
compare_option = tk.StringVar(value="column") # 默认按列比对
tk.Radiobutton(compare_col_frame, text="按列", variable=compare_option, value="column").grid(row=0, column=2, padx=5, pady=5)
tk.Radiobutton(compare_col_frame, text="按行", variable=compare_option, value="row").grid(row=0, column=3, padx=5, pady=5)
col1_label = tk.Label(compare_col_frame, text="列1名称/行号1(用逗号分隔):")
col1_label.grid(row=1, column=0, padx=5, pady=5)
col1_entry = tk.Entry(compare_col_frame)
col1_entry.grid(row=1, column=1, padx=5, pady=5)
col2_label = tk.Label(compare_col_frame, text="列2名称/行号2(用逗号分隔):")
col2_label.grid(row=2, column=0, padx=5, pady=5)
col2_entry = tk.Entry(compare_col_frame)
col2_entry.grid(row=2, column=1, padx=5, pady=5)
compare_cols_button = tk.Button(compare_col_frame, text="比对", command=compare_uploaded_multiple)
compare_cols_button.grid(row=3, column=0, columnspan=4, pady=10)
# 结果显示部分
result_frame = tk.Frame(root)
result_frame.pack(pady=10, padx=10, fill="both", expand=True)
scrollbar = tk.Scrollbar(result_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
result_text = tk.Text(result_frame, wrap='word', height=20, width=100, yscrollcommand=scrollbar.set)
result_text.pack(pady=10, padx=10, fill="both", expand=True)
scrollbar.config(command=result_text.yview)
# 运行主循环
root.mainloop()
结语:写代码就像做饭,关键是有趣!
今天,我们用Python打造了一个强大的Excel比对工具,从选择文件到生成报告,每一步都在你的掌控之中。这不仅仅是代码,它是你工作中的得力助手,也是你编程技能的彰显。下次再遇到两个Excel文件不一致的情况,你知道该做什么了——掏出你的“神器”,让它们无所遁形!
赶紧试试吧,用代码让生活更轻松,也更有趣!Happy coding! 😎