前言
这篇为游戏策划配置表测试系列的第二篇,主要讲的是根据项目的配置表格规范或格式来读取对应的数据,比如读取Excel文件的所有数据、读取当个工作表的数据、按行读取、按列读取、按字段读取,用不同的库读取速度也不一样,之前也写过一篇读取速度对比的文章,这里跳转,我这主要用的openpyxl,其它几个库使用上差别不大;另外一个会用到Lua库,因为配置表里的数据有些直接配置成Lua的table或array,需要转换成Python的数据。
技术栈
语言:Python3.6.9
库:openpyxl、lupa、pathlib、datatime
openpyxl、lupa安装:
pip install openpyxl
pip install lupa
配置Demo
正文
一、工作表数据读取
按行读取的配置表结构看下图,参考一下
来实现一个方法获取对应的数据,
参数注释
table:Excel文件名
sheet_name:工作表名
start_row:从第几行开始读取,初始值是0
变量注释
self.floder_path:配置表文件夹路径
column_value:openpyxl读取后是cell对象,需要通过.value来获取
from openpyxl import load_workbook
def get_sheet_data(self,table='道具配置表.xlsx',sheet_name='',start_row=6):
'''获取工作表的数据'''
wb = load_workbook(filename=f'{self.folder_path}{table}', data_only=True)
sheet_datas = {}
ws = wb[sheet_name]
for column in list(ws.columns)[1:]:
column_value = [i.value for i in column]
sheet_datas[column_value[start_row]] = column_value[start_row+1:]
return sheet_datas
二、工作表屏蔽
上面的数据是没有经过筛选的,当出现不用的数据或工作表策划没有移除的时候,需要把这些数据屏蔽掉
参数注释
ws = wb[sheet]
变量注释
discard_sheet:列表的第一个值是Excel文件名,第二第三及后面的为工作表名,支持多个工作表屏蔽
单元格A3B3:用来标识工作表是按行还是按列来读取文件
def _delete_discard_sheet(self,ws:any,sheet:str,table:str):
'''屏蔽那些废弃的表'''
discard_sheet = [['cfgDemo', 'baseRow', 'baseColumn']]
if ws['B3'].value in [None,''] or ws['A3'].value != 'key数量' or '废弃' in sheet \
or '不用' in sheet or '停用' in sheet:
return False
if table == 'cfgDemo.xlsx':
# sheet主题颜色为1表示弃用
sheet_prop = ws.sheet_properties.tabColor
if sheet_prop:
if sheet_prop.theme == 1:
return False
for ds in discard_sheet:
if ds[0] in table:
for sh in ds[1:]:
if sh == sheet:
return False
三、筛选有效数据
我们除了要屏蔽工作表之外,可能还有很多冗余数据需要额外处理,比如一些废弃的字段;第N行,第N列的数据是不再引用的,需要兼容;这个方法要用到numpy的数组,相关的知识这里简单介绍下,需要更全面的自行百度哈。
np.delete:axis=0,按行删除
np.delete:axis=1,按列删除
np.where(condition, x, y):满足条件(condition),输出x,不满足输出y。
这里的参数与上面说的参数一致,不重复了
变量注释
**key_data:**我这里的KEY值也代表着从第几列作为有效数据的参考,例如KEY=2,有效列是B列。
where_none: 用来找出key_data空值的第一维坐标,然后移除无用数据
**思路:**按行来遍历整个工作表,碰到整行都是空的就停止,读完的数据转换成数组,再根据key值利用numpy.delete来删除无用的数据
import numpy as np
def _get_valid_rows(self,ws,table='',start_row=5):
#key=0 按行读取,key>0 按列读取
tag_row = ws['B3'].value
try:
if not isinstance(eval(str(tag_row)),int) or tag_row > 4:
print('sheet key is empty', tag_row, table)
return False
except:return False
def get_data(srow=start_row):
rows_data = []
for row in list(ws.rows)[srow:]:
one_row_data = [i.value for i in row]
if len(set(one_row_data)) == 1 and list(set(one_row_data))[0] == None:
break
else:rows_data.append(one_row_data)
return rows_data
if tag_row != 0:
data = np.array(get_data(),dtype=object)
key_data = data[:,tag_row - 1]
# 根据Key行来找出废弃行的位置
where_none = np.where(key_data == None)
data = np.delete(data,where_none[0][:],0)
return data
else:
data = np.array(get_data(5), dtype=object)
if len(data) > 0:
key_data = data[:, 2]
where_none = np.where(key_data == None)
data = np.delete(data, where_none[0][:], 0)
return (data,)
else:return False
四、读取所有数据
上面定义两个方法 delete_discard_sheet 和 get_valid_rows都在这里调用,这个函数利用sheetnames来获取所有的工作表名,结合两个方法遍历每个工作表,把所有数据存在变量all_sheet_data,方法最后return这个变量
变量注释
read_only: openpyxl的只读模式运行速度会快一些,需要了解的可以看我写的速度对比的文章
output_arg: 导入参数,用来区分前后端导出,如果该值为空则不读取数据
def get_all_sheet(self,table='道具配置表.xlsx'):
'''获取整个表格所有的sheet'''
self.all_sheet_names = {}
wb = load_workbook(filename=f'{self.folder_path}{table}', data_only=True, read_only=True)
all_sheet_data = {}
for sheet in wb.sheetnames:
sheet_datas = {}
ws = wb[sheet]
if self._delete_discard_sheet(ws,sheet,table) == False:
continue
valid_data = self._get_valid_rows(ws,table=table)
if isinstance(valid_data,bool) or len(valid_data) <= 0:
continue
if not isinstance(valid_data,tuple):
if len(valid_data) <= 2:
continue
output_arg = valid_data[0]
# 获取字段
field_list = valid_data[1]
# 根据每字段列来插入数据
for i in range(1,len(field_list)):
col_data = list(valid_data[2:,i])
if len(col_data) > 0 and field_list[i] != None and output_arg[i] in ['sc','c','s']:
sheet_datas[field_list[i]] = col_data
else:continue
else:
val_data = valid_data[0]
for row_data in val_data:
if len(row_data[1:4]) > 0:
sheet_datas[row_data[2]] = [row_data[3]]
else:continue
if len(sheet_datas) > 0:
all_sheet_data[sheet] = sheet_datas
self.all_sheet_names[sheet] = ws['B2'].value
else:
print('debug-> ', table, sheet)
wb.close()
return all_sheet_data
读完之后的数据结构,{“工作表”: {“字段名”: “字段数据”}…}
五、Lua数据处理
主要是针对lua的table和array来进行处理,转换成Python的字典,如果不需要转换则忽略。
from lupa import LuaRuntime
lua = LuaRuntime(unpack_returned_tuples=True)
lua.eval("{{type=0,id=1,count=1}}")
执行一下看看,从Excel读出来的字符串,转换到lua的table,再转换成Python的dict,这里的数据转换可以在读取表格数据时使用,也可以进行断言的时候使用。
>>>import lupa
>>>lua = lupa.LuaRuntime(unpack_returned_tuples=True)
>>>cell_data = "{{type=0,id=1,count=1}}"
>>>type(cell_data)
<class 'str'>
>>>lua_table = lua.eval(cell_data)
>>>type(lua_table)
<class 'lupa._lupa._LuaTable'>
>>>lupa.lua_type(lua_table)
'table'
>>>to_dict = dict(lua_table[1])
>>>to_dict
{'id': 1, 'count': 1, 'type': 0}
结语
上面这些都是前提准备工作,其实和策划配置表导出工具有些类似,之前也考虑要不要自己写脚本来读取配置表,直接用导表工具导出来后再进行读取,这样子省事,后面经过一轮比较后还是采取自己编写,自由度高一些。
欢迎小伙伴关注微信公众号ID:gameTesterGz
或关注我的CSDN:https://blog.csdn.net/qq_32557025
谢谢各位的关注、点赞!