记录一下最近在研究的一个程序,如何实现两个Excel表格之间的内容对比。之前做出来的版本1,版本2都存在较大的逻辑问题,使用的openpyxl第三方库,没有透彻理解到openpyxl的特性,导致程序无法对比多数据量多的文件;在一些不对齐表格的对比上也出现了一些严重问题,出现IndexError、ValueError等一些bug。现在改进了效率,也修复了处理主副文件行数不同列数不同的问题
使用环境:Python3.8 + openpyxl 3.0.9 + PyQt5 5.15.4 + Pandas 1.3.5
pandas处理DataFrame
def fill_rows(df1, df2, df1_rows, df2_rows):
"""
解决 dataframe行数不一的问题
:param df1: 主文件读取的dataframe
:param df2: 副文件读取的dataframe
:param df1_rows: 主文件的最大行数
:param df2_rows: 副文件的最大行数
:return: 处理完的df1,df2
"""
df1_cols, df2_cols = df1.shape[1], df2.shape[1]
if df1_rows > df2_rows:
n = df1_rows - df2_rows # 获取缺失行数
for i in range(n):
df2.loc[i + 2] = ["~\\" for i in range(df2_cols)] # 列表推导式补全
return df1, df2
elif df1_rows < df2_rows:
n = df2_rows - df1_rows
for i in range(n):
df1.loc[i + 2] = ["~\\" for i in range(df1_cols)]
return df1, df2
else:
return df1, df2
def fill_cols(lst1, lst2):
"""
处理读取的列数据,长度不一问题
:param lst1: 主文件读取的一行数据
:param lst2: 副文件读取的一行数据
:return:
lst1:处理后的lst1
lst2:处理后的lst2
len1/len2:返回最大长度
"""
len1 = len(lst1)
len2 = len(lst2)
if len1 > len2:
for i in range(len1 - len2):
lst2.append("~\\") # 填充特殊字符
return lst1, lst2, len1
elif len1 < len2:
for i in range(len2 - len1):
lst1.append("~\\")
return lst1, lst2, len2
else:
return lst1, lst2, len1
在最后设置单元格背景时,如果文件打开会产生 PermissionError异常,因此读取数据的时候需要判断文件是否被打开
def check_PermissionError(path):
"""
通过文件是否可以打开 判断文件是否打开
:param path: 文件路径
:return: None
"""
try:
f = open(path, "a")
except OSError as e:
return False
else:
f.close()
return True
获取单元格范围内的所有单元格坐标
为什么要获取合并单元格范围?在最后获取到 fill_cell中,如果包含合并单元格,在设置背景颜色时不出现效果,
因此需要获取此异同单元格所在的合并单元格范围,然后将整个范围都设置背景颜色
def dudge_merged(cell):
"""
返回某个合并范围内的所有单元格
:param cell:合并范围
:return: cells_list:拆解的合并单元格坐标
"""
# 百度中提及的判断单元格是否为合并单元格,未实现效果
# if type(cell).__name__ == 'MergedCell':
# print("True")
cells_list = [] # 存储单元格坐标
lst = cell.split(':')
start, end = lst[0], lst[-1] # 获取头字母,尾字母
start_letter, start_number = ord(start[0]), int(start[1:]) # 转换为ASCII码
end_letter, end_number = ord(end[0]), int(end[1:]) # 获取头数字,尾数字
if start_letter == end_letter: # 判断是否为横向合并
for i in range(end_number - start_number + 1): # 根据头尾数字差 获取列数
cells_list.append(chr(start_letter) + str(start_number + i))
else:
col = column_index_from_string(end[0]) - column_index_from_string(start[0]) + 1 # 计算列数
row = end_number - start_number + 1 # 计算行数
col_list = [chr(start_letter + i) for i in range(col)] # 生成范围内的所有列字母
row_list = [start_number + i for i in range(row)] # 生成范围内的所有行号
for c in col_list:
for r in row_list:
cells_list.append(c + str(r))
return cells_list
def get_merage_list(merage): # 与dudge_merged()配合使用
"""
返回所有合并单元格范围内的所有单元格
:param merage: 合并单元格合集
:return: 扩充的单元格
get_merage_list('A1:A3') --> return: [['A1','A2','A3']]
get_merage_list('A1:B2') --> return: [['A1','A2','B1','B2']]
"""
merged_cells = []
if len(merage) > 0:
for range_cell in merage:
values = dudge_merged(str(range_cell))
merged_cells.append(values)
else:
values = dudge_merged(str(merage[0]))
merged_cells.append(values)
return merged_cells
设置单元格背景颜色
def set_cellcolor(wb, sheet, fill_list, path1, path2, fgColor="FFFF0000"):
"""
设置一组单元格背景颜色
:param wb: openpyxl.workbook对象
:param sheet:Worksheet 对象
:param fill_list:填充单元格集
:param path1:主文件路径
:param path2:副文件路径
:param fgColor:背景颜色
:return:None
"""
fill = PatternFill("solid", fgColor=fgColor) # 设置样式
for cell in fill_list:
sheet[cell].fill = fill # 遍历集合填充
if check_PermissionError(path1) and check_PermissionError(path2): # 判断是否打开
wb.save(path1) # 保存文件
def set_acellcolor(wb, sheet, cell, path1, path2, fgColor="FFFF0000"):
"""
设置单个单元格背景颜色
:param wb: openpyxl.workbook对象
:param sheet:Worksheet 对象
:param cell:准备填充的单个单元格
:param path1:主文件路径
:param path2:副文件路径
:param fgColor:背景颜色
:return:None
"""
fill = PatternFill("solid", fgColor=fgColor) # 设置样式
sheet[cell].fill = fill
if check_PermissionError(path1) and check_PermissionError(path2): # 判断是否打开
wb.save(path1) # 保存文件
判断文件内容读取判断
def judege_cols(self, master_cols, other_cols, max_rows, wf1, wf2):
"""
判断文件内容读取判断
:param master_cols: 主文件列数
:param other_cols: 副文件列数
:param max_rows: 最大列数
:param wf1: 主文件对象
:param wf2: 副文件对象
:return: 异同单元格合集
"""
fell_list = [] # 异同单元格合集
for i in range(max_rows):
# 逐行读取数据;如果行数不一会,短的一方会报IndexError
master_lst, other_lst = [], []
try:
master_lst = list(wf1.values[i])
except IndexError:
master_lst = (["~\\" for i in range(other_cols)])
try:
other_lst = list(wf2.values[i])
except IndexError:
other_lst = (["~\\" for i in range(master_cols)])
# 如果主文件读取的一行与副文件读取的一行,长度不一,则需要补齐,不然会报IndexError
if len(master_lst) != len(other_lst):
master_lst, other_lst, lenl = WtoolsFuc.fill_cols(master_lst, other_lst) # 补齐列
if not eq(master_lst, other_lst): # 具体判断内容
for n in range(lenl):
if not eq(master_lst[n], other_lst[n]):
fell_list.append(get_column_letter(n + 1) + str(i + 1)) # 返回坐标
return fell_list
判断异同单元格是否为合并
"""
判断异同单元格的是否为合并单元格
falsecell_count: 异同单元格数量
merage:全文件中所有单元格合集
merage_count:合并单元格数量
"""
if falsecell_count > 0:
if len(merage) > 0:
merged_cells = WtoolsFuc.get_merage_list(merage) # 返回文件中合并单元格的具体范围
for merged_cell in merged_cells:
for fi_cell in fill_cell:
if fi_cell in merged_cell:
merage_count += 1
WtoolsFuc.set_cellcolor(master_wb, master_sheet, merged_cell, path1, path2,
fgColor="1874CD") # 异同单元格设置为蓝色
else:
WtoolsFuc.set_acellcolor(master_wb, master_sheet, fi_cell, path1, path2)
else:
WtoolsFuc.set_cellcolor(master_wb, master_sheet, fill_cell, path1, path2)
实现button点击打开文件
def open_master_file(self):
"""
打开主文件
:return: None
QFileDialog.getOpenFileName(self, 文件选择框标题, 打开的默认路径, 过滤文件后缀)
返回:('C:/Users/Meaauf/Desktop/p7.xlsx', 'Excel Files(*.xlsx)')元组
"""
self.lineEdit.setText(QFileDialog.getOpenFileName(self, "选择主文件", "", "Excel Files(*.xlsx)")[0])
设置下拉列表框
def master_combobox(self):
"""
设置下拉列表框值
:return: None
"""
if self.comboBox.count() > 0:
self.comboBox.clear()
wb = openpyxl.load_workbook(self.lineEdit.text(), read_only=True)
self.comboBox.addItems(wb.sheetnames)
-w:不使用窗口打开,如果不添加-w,会在运行程序时打开控制台,显示print信息
-p:python会指定寻找路径下的第三方库打包
-i:设置exe可执行文件的图标
【在打包后,会在目录下显示一个dist文件夹,将图标文件以及使用的一些图片资源都放在里面】
pyinstaller -w -F -p C:\Users\Meaauf\AppData\Local\Programs\Python\Python38\Lib\site-packages -i favicon.ico Wtools.py WtoolsFuc.py
相关源码及程序
GitHub: https://github.com/Meaauff/Wtools.git
CSDN : https://download.csdn.net/download/weixin_45564816/77696529