一览
问题
在研发过程中,通常会定义一些Excel表格,规定行列值让策划填写,然后,转成lua的table文件,直接使用。
但是,随着研发进行,项目迭代,表格将越来越大。
如果表格中存在大量重复数据,或者表格中很多列数值重复,则可以通过数据压缩给表减减肥。
解决
利用python实现lua表的数据压缩
- excel表内存在大量 同列不同行 内容一致
- excel表内存在大量 复合型 单元格内容一致
具体代码,均在github中: ltree98’s github -> CompressLua
参照方案
这是我在网上看到的文章:
总结的来说,就是
- 利用lua的元表机制。如果在table中取的值不存在,会去它的元表中查找(如果元表存在)。
- 将重复的table,提取出来,将所有使用的地方引用过去。
问题
在参照方案的文章中,也提供了解决方法,但是在使用过程中遇见一些问题。
对同一文件压缩后文件MD5可能不一致
对同一个lua文件,进行减肥,在减肥后,虽然最终体重一样,但是可能这次瘦了肚子,下次瘦了腿。
主要原因就是参照方案使用lua处理,对同一个table遍历顺序是不稳定的,即table中存在 A B C 元素,可能这次遍历顺序是 A-B-C,下次遍历顺序可能就是 B-A-C;稳定性得不到保证。
lua的官网对于table的遍历描述一直是:
The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the
ipairs
function.)
就是 索引在遍历过程中的次序是不固定的,即使是数字索引也不固定。
因为,项目中使用热更机制是比较两个文件的MD5,决定是否更新该文件。
所以,这个问题就显得很严重,每次热更要更新所有的表。当然,这个问题也可以通过做备份等方式来弥补,但是毕竟治标不治本。
但是用不同的lua版本发现:
使用 lua5.1 版本 生成的压缩文件是一致的
使用 lua5.3版本 生成的压缩文件是不一致的
复杂度高
虽然是现成的,但是也没法直接拿来用;
还是要根据项目现状进行修改。
在整合过程中,发现逻辑比较复杂,而且工具集与我们现有的python不符。(如果用其他工具集,又要去配置相应环境等,比较麻烦)。
修改方案
简介
目的
- 解决 Excel表 内存在大量 同列不同行 内容一致
- 解决 Excel表内存在大量 复合型单元格 内容一致
设计
- 对于重复table,只存一份,其他引用过去
- 设计一个基础元表,存储Excel每列 最频繁的值;其他表缺省 最频繁的值
- 设置 只读属性
注意
- Lua作用域内存放最大的local变量个数为 200个,超过的需要放入临时数组
- 输出到lua文件,key值不可存在特殊字符($、- 等)
缺点
- Lua表的可读性变差,需要跳转获取最终值
- 使用元表实现,若后续处理(比如 加密,修改操作 等)也存在使用元表,增加处理的复杂度
方案
名词解释
-
Excel表:Excel转换成Lua的表,一般结构为
-- tableName.lua local tableName = { ... } return tableName
-
默认表:未来的元表,存储Excel中每列最频繁的元素
-
重复表:Excel表中各单元格重复的 复合型元素(array/table)
流程
-
遍历表,进行统计
-
统计Excel表中各列元素出现次数
-
统计Excel表中 单元格复合型元素 出现次数
-
-
构造 重复表
- 筛选 单元格复合型元素 次数大于1的元素,构建重复表
-
构造 默认表
- 根据各列最频繁元素,构造默认表
-
整理 Excel表
- 根据默认表,将重复的字段忽略
-
输出 各表
- 根据 重复表,替换并输出 重复表
- 根据 重复表,替换并输出 Excel表
- 根据 重复表,替换并输出 默认表
-
设置 元表 及 只读属性
关键代码
最新代码请移步 lt-tree的github
###################################################################
##
## tools
##
def count_dict_deep(dict_temp):
"""Count dictionary's deep
Args:
dict_temp: dict
Returns:
int : deep
"""
deep = 0
for item in dict_temp.values():
if isinstance(item, dict):
temp_deep = count_dict_deep(item)
if temp_deep > deep:
deep = temp_deep
return deep+1
def calc_weight(obj1):
"""Calculate obj's weight
Args:
obj1: tuple[dict's string, dict's frequency]
Returns:
int : weight
"""
dict1 = eval(obj1[0])
times1 = obj1[1]
deep1 = count_dict_deep(dict1)
ans = deep1 + 1/times1
return ans
def count_table_frequency(unit_dict, dict_frequency):
"""Count table frequency
Count table frequency and record as {table string: times}
Args:
unit_dict: dict, need analyse data
dict_frequency: dict, the record's set
"""
unit_str = str(unit_dict)
if unit_str in dict_frequency:
dict_frequency[unit_str] = dict_frequency[unit_str] + 1
else:
dict_frequency[unit_str] = 1
# traversing sub dict
for item in unit_dict.values():
if isinstance(item, dict):
count_table_frequency(item, dict_frequency)
def count_table_value_frequency(key, value, item_frequency):
"""Count table value frequency
Count every excel column element appear times.
Record as {
key1 : {element1 : times, element2: times, ...}
key2 : {element1 : times, element2: times, ...}
...
}
Args:
key: string
value: string or dict
item_frequency: dict, the record's set
"""
if isinstance(value, dict):
value = str(value)
if key in item_frequency.keys():
if value in item_frequency[key].keys():
item_frequency[key]