quick-x.客户端框架之excel表转换解析成lua文件

/ Quick-x客户端框架Luapython / 没有评论 / 80浏览

quick-x-05.客户端框架之excel表转换解析

解析约定

Excel导出的Txt的文本,约定

  1. 第一行为字段类型,
  2. 第二行为字段名称,
  3. 第三行以后'#'开头为字段注释,非'#'开头则为字段内容

定制客户端所需的配置表文件、配表对应的实体文件

lua配置表,和lua配置表对应的实体文件。都是通过python解析对应的txt文本来获得。

客户端配表文件

期望的客户端配置表格式

我期望的txt转成的lua配置表格式为

local 配置表表名 = {
	{"第一行第一个字段的值", "第一行第二个字段的值", "第一行第三个字段的值"...},
	{"第一行第一个字段的值", "第一行第二个字段的值", "第一行第三个字段的值"...}
}
return 配置表表名

在开发过程中,可能会遇到某个字段是多个值,所以我们在excel中填充字段时,遇到字段为多个值时,用'|'来分隔开。

最终的txt转成的lua配置表格式应该是这样的

local 配置表表名 = {
	{"第1行第1个字段的值", {"第1行第2个字段的1值","第1行第2个字段的2值"...}, "第1行第3个字段的值"...},
	{"第2行第1个字段的值", "第2行第2个字段的值", "第2行第3个字段的值"...}
}
return 配置表表名

利用python将txt转成期望的lua配表格式

操作记录:

  1. 按行读取文本数据,将每一行数据存在数组里(这里需要注意编码类型)
  2. 剔除变量类型行、变量名称行、变量注释行
  3. 将内容数据存到数组中,递归拼接字符写入到lua文件中

lines = [] # 转载该所有行
fp = open(filePath, "r")
line = fp.readline()
while len(line) != 0:
	line = line.replace('\r','')
	line = line.replace('\n','')
	lines.append(line) # 这里需要注意编码
	line = fp.readline()
fp.close()

# 排除变量类型行数据
column = len(lines[0]) 获取多少的个字段
del lines[0]

# 排除变量名字行数据
del lines[0]

# 排除注释行(注释行行数不定)
noteList = []
while len(lines) >0 and lines[0].startswith('#'):
	del lines[0]

# 将每一行数据存放到数组里面,若单元格为数组,则嵌套数组如:
# [
#	["第1行第1个字段","第2行第2个字段"],
# 	["第1行第1个字段",["第2行第2个字段第1个值","第2行第2个字段第2个值"]],
# ]
contentList = []
for contentLine in lines: # 遍历行数据
	contentLineList = contentLine.split('\t') # 分割字符
	contentLineList = contentLineList[0:column]
	if contentLineList[0] != '': # 首元素不能为空
		tmpContentLineList = []
		for oneTab in contentLineList:
			if oneTab.find('|') != -1: # 单元格为table
				oneTabList = oneTab.split('|')
				if oneTabList[0] == '':
					del oneTabList[0]
				tmpContentLineList.append(oneTabList)
			else:
				tmpContentLineList.append(oneTab)
		contentList.append(tmpContentLineList)

# 将list转成lua table
def listToLuaTabStr( oneList ):
	retStr = '{'
	for oneTab in oneList:
		if isinstance(oneTab,(list)) == True: # 如果是list就递归拼接
			retStr += listToLuaTabStr(oneTab)+','
		else:
			oneTab = oneTab.replace('\"','\\\"')
			retStr += '\"' +  oneTab + '\",'
	retStr += '}'
	return retStr

luaFp = codecs.open("路径+导出的lua文件名.lua","w","utf-8")
luaFp.write('local ' + "表名" + ' = \n{\n')
for oneList in contentList:
	writeStr = listToLuaTabStr(oneList)
	luaFp.write('\t'+writeStr+',\n')

luaFp.write('\n}\nreturn ' + dataName)
luaFp.close()

客户端lua配表对应的实体lua文件

期望的客户端lua配置表对应的实体lua文件格式为

我期望的lua表对应的lua实体文件格式为

配置表表名 + "Lib" = class(配置表表名 + "Lib")
	
local 数据列表 = nil -- 数据队列
local 文件名称 = nil -- 文件名(不含扩展名)

-- 构造函数
function 配置表表名 + "Lib":ctor(list, fileName)
	self.字段名称1         = list[1]      -- 字段注释
	self.字段名称2         = list[2]      -- 字段注释

	self.key              = self.字段名称1  -- dataList存储key,key作为表索引
	
	if self.custom_ctor ~= nil then -- 自定义 ctor 部分
		self:custom_ctor()
	end
end

-- 注册函数
function 配置表表名 + "Lib".processData(dir, file)
	数据列表,文件名称 = LibraryManager.load(dir, file, 配置表表名 + "Lib")
end	

-- 根据id获取条目
function 配置表表名 + "Lib".getDataById(id)
	return 数据列表[id]
end

-- 获取所有条目
function 配置表表名 + "Lib".getDataList()
	return 数据列表
end

----------------------------BEGIN-------------------------------
-- 给用户留自定义部分。下次生产解析文件,不会被覆盖
-- TODO
-----------------------------END--------------------------------

return 配置表表名 + Lib
  1. 首先要保存用户自定义部分,保证下次字段修改,再次生成lua解析配表不会将用户自定义部分给覆盖掉。
  2. 其次提供几个公用函数,每个lua表对应的实体都会有的getDataById函数和getDataList函数。
  3. 最后就是字段名称和字段值、字段索引的行对应
  4. LibraryManager中对lua配表进行实体对象创建操作,下面会记录到

利用python写入对应的数据实体类

操作记录:

  1. 实体文件存在则先保存用户自定义部分内容,记录到数组中
  2. 写入变量名、变量值、变量注释。
  3. 将之前保存的用户自定义部分内容,从新写到实体文件中

# 写程序lib文件
def writeLuaLibFile(dataName,typeList,nameList,noteList):
	libClassName = dataName+'Lib'
	libFilePath = os.path.join(libDir,dataName+'Lib.lua')

	# 如果文件存在,需要保存用户自定义的内容
	extLines = []
	isExtLine = False
	if os.path.exists(libFilePath):
		readFp = codecs.open(libFilePath,"r","utf-8")
		oneLine = readFp.readline()
		if len(oneLine) != 0:
			# 去掉文件头说明
			oneLine = readFp.readline()
		while len(oneLine) != 0:
			if isExtLine == True:
				extLines.append(oneLine)
				if oneLine.find('START EXT END') != -1:
					isExtLine = False
					break
			else:
				if oneLine.find('START EXT BEGIN') != -1:
					extLines.append(oneLine)
					isExtLine = True
			oneLine = readFp.readline()
		readFp.close()

	libFp = codecs.open(libFilePath,"w","utf-8")
	libFp.write(libClassName + ' = class(\"' + libClassName + '\")\n\n')
	libFp.write('local dataList = nil -- 数据队列\n')
	libFp.write('local fileName = nil -- 文件名(不含扩展名)\n\n')

	libFp.write('-- 构造函数\n')
	libFp.write('function %s:ctor(list, fileName)\n' % libClassName)
	num = len(nameList)
	for i in range(0,num):
		oneProperty = nameList[i]
		leq = '\tself.%s' % oneProperty
		libFp.write(leq.ljust(30)+'= ') # 写字段
		if typeList[i] == 'INT':
			req = 'tonumber(list[%d])' % (i+1)
			libFp.write(req.ljust(25)+'-- ') # 写值
		else:
			req = 'list[%d]' % (i+1)
			libFp.write(req.ljust(25)+'-- ')
		# 写注释
		for note in noteList:
			libFp.write(note[i] + ' ')
		libFp.write('\n')

	libFp.write('\n')
	leq = '\tself.key'
	sKey = 'self.%s' % nameList[0]
	libFp.write(leq.ljust(30)+'= ' + sKey.ljust(25) + '-- dataList存储key\n')

	libFp.write('\t-- 自定义 ctor 部分\n')
	libFp.write('\tif self.custom_ctor ~= nil then\n')
	libFp.write('\t\tself:custom_ctor()\n')
	libFp.write('\tend\n')

	libFp.write('end\n\n')
	
	写入processData方法...
	#processData()

	写入getDataById方法...
	#getDataById()
	
	写入getDataList方法
	#getDataById()

	# 自定义扩展区间
	if len(extLines) == 0: # 原来的文件中不存在用户自定义代码
		libFp.write('----------------------------BEGIN-------------------------------\n')
		libFp.write('-- 给用户留自定义部分。下次生产解析文件,不会被覆盖\n\n')
		libFp.write('-----------------------------END--------------------------------\n\n')
	else: # 存在则将之前的用户自定义写入到新文件中
		for extLine in extLines:
			libFp.write(extLine)

	libFp.write('return ' + libClassName)
	libFp.close()

LibraryManager文件中加载配表、创建配表模型对象


LibraryManager = {}

--加载配表
--@param dir 		lua配表文件路径
--@param file 		lua配表文件名
--@param libClass	映射Class
--@return 返回分离数据table(table)、返回对应加载的文件名
function LibraryManager.load(dir, file, libClass)
	local tableData = require(dir .. file)
	local fileName = string.sub(file, 1, #file-#".lua") -- 截取文件名剔除.lua后缀
	local lib = {}
	local cls = nil
	for i = 1, #tableData do
		cls = libClass.new(tableData[i]) -- 每一行数据填充一个实体对象
		if lib[cls.key] ~= nil then
			SYSLog("##########Error:Key is not only, fileName:" .. fileName .. ", id:" .. tostring(cls.key))
		end
		lib[cls.key] = cls -- key做表索引
	end
	return lib, fileName
end

-- 加载所有配表
function LibraryManager.init()
	SYSLog("LibraryManager init start")
	ConfigLocalTextDataLib.processData(Config.CONFIG_PATH, "ConfigLocalTextData.lua") -- 多语言表
	-- TODO... other table data
	SYSLog("LibraryManager init end")
end

return LibraryManager
  1. LibraryManager.init()函数在进入游戏调用,来加载所有配置表,转成对象表模式
  2. LibraryManager.load()函数将每一行数据创建一个对应的实体对象,并返回一个实体表

遇到的坑

  1. python在读取文件时(unicode(line, 'utf-8').decode('utf-8')),需要十分注意编码格式。excel中导出Txt一般为Unicode编码,这种编码在python中读取会报错。在excel中导出utf8操作实在是繁琐,所以利用vbs来一键导出ANSI编码的Txt
点击这里给我发消息
嘿!有什么能帮到您的吗?