关于这个问题,几年前已经做过一个工具,自动导出成一个c++的struct,然后用vector存储这些数据,然而在实践中发现在遇到多层嵌套时,这种数据表现是非常乏力的。比如掉落表会配多个掉落物,每个掉落物都有数量、概率等。那么最好是将物品ID,数量,概率设置为一个struct然后用一个vector存储,而不是用drop_id1, drop_id2, drop_num1,drop_num2来表示,类似这样的情况是很常见的。后来工作中用Lua比较多,也曾写过一个将表导出lua的工具,对上述问题的解决方案是给每一张表按照需要写一套转换代码生成一张table表。其实也是挺麻烦的。如果给列头设置一些规则,那么可以以尽量少的代码,就自动生成一个lua表出来,那就方便多了。大致分为以下几个步骤:
一、设置列头结构化的规则,比如有数据表player.xlsx数据如下结构
id name skill_id skill_level skill_id2 skill_level2 buff_id1 buff_id2 reward
401 张三 101 10 102 10 1001 1002 (1002, 20),(1003, 20)
结构化的规则如下:
1 head_struct = { 2 id = id, 3 name = name, 4 skill = { 5 [1] = { 6 id = skill_id, 7 level = skill_level, 8 }, 9 [2] = { 10 id = skill_id2, 11 level = skill_level2, 12 } 13 }, 14 buff = { 15 [1] = buff_id1, 16 [2] = buff_id2, 17 }, 18 reward = array(reward , { 19 id = 1, 20 count = 2, 21 }), 22 }
key值为导出后的key值,value值为数据表的列名。array函数将某列的形如(1002, 20),(1003, 20)值转换为reward = { [1] = {id = 1002, count = 20}, [2] = {id = 1003, count = 20}}表结构。这样就基本满足我想要的表现了。
二、将xlsx导出为一个简单的lua table,表名就是取出xlsx的sheet名再上.tmp.lua后缀即可。,上述表就会导出为player.tmp.lua
player = { [1] = { id = 401, name = ‘张三‘, skill_id = 101, skill_level = 10, skill_id2 = 102, skill_level2 = 10, buff_id1 = 1001, buff_id2 = 1002, reward = ‘(1002, 20),(1003, 20)‘, }, }
要导出为这样的表,还需要给策划设置三个规则:
1.xlsx表第一行默认为列的类型,类型共三种(number, string, bool)可以缩写为n,s,b。为了兼容我自己的情况,如果类型写为int, float, i也会认为是number,其它非number, string, bool 类型的都视为string类型。
2.xlsx表第二行默认为列名。
3.如果某列名为valid,那么true或者1表示为该行数据需要导出,否则表示该行数据不需要导出。
导出代码我是用GO语言写的,代码如下:
1 /*------------------------------------------------------------------ 2 // 著作版权:Copyright (C) liuxb 3 // 创建时间:[liuxb|20160704] 4 // 功能描述:xlsx表导出 5 // 6 // 修改时间: 7 // 修改描述: 8 // 9 //----------------------------------------------------------------*/ 10 11 package main 12 13 import ( 14 "flex/log" 15 "github.com/tealeg/xlsx" 16 "os" 17 "strings" 18 "strconv" 19 ) 20 21 type column struct { 22 name string 23 type_ string 24 } 25 26 type table struct { 27 columns []column 28 rows [][]string 29 } 30 31 var valid string 32 33 func init() { 34 valid = "valid" 35 } 36 37 func getStrings(cells []*xlsx.Cell) []string { 38 str := make([]string, 0) 39 for _, cell := range cells { 40 s, err := cell.String() 41 if err != nil { 42 log.Error(err.Error()) 43 s = "" 44 } 45 str = append(str, s) 46 } 47 return str 48 } 49 50 func getColumn(types []string, names []string) []column { 51 cols := make([]column, 0) 52 if len(types)<len(names) { 53 for i := len(types); i<len(names); i++ { 54 types = append(types, "s") 55 } 56 } 57 for i := 0; i<len(types); i++ { 58 types[i] = strings.ToLower(types[i]) 59 switch types[i] { 60 case "n": 61 case "i": 62 types[i] = "n" 63 case "int": 64 types[i] = "n" 65 case "float": 66 types[i] = "n" 67 case "number": 68 types[i] = "n" 69 case "b": 70 case "bool": 71 types[i] = "b" 72 case "string": 73 types[i] = "s" 74 default: 75 types[i] = "s" 76 } 77 } 78 for i:= 0; i<len(names); i++ { 79 if names[i] == "" { 80 break 81 } 82 col := column{ 83 name : names[i], 84 type_: types[i], 85 } 86 v := strings.ToLower(col.name) 87 if v == valid { 88 col.name = v 89 } 90 cols = append(cols, col) 91 } 92 return cols 93 } 94 95 func getRow(cols []column, vals []string) []string { 96 if len(vals)<len(cols) { 97 for i := len(vals); i<len(cols); i++ { 98 vals = append(vals, "") 99 } 100 } 101 if len(vals)>len(cols) { 102 vals = vals[0:len(cols)] 103 } 104 for i:= 0; i<len(cols); i++ { 105 if cols[i].name == valid { 106 v := strings.ToLower(vals[i]) 107 if v == "false" || v == "0" { 108 return nil 109 } 110 } 111 } 112 return vals 113 } 114 115 func getTable(start int, rows []*xlsx.Row) *table { 116 if len(rows)<4 || len(rows)<start { 117 return nil 118 } 119 t := new(table) 120 t.rows = make([][]string, 0) 121 122 types := getStrings(rows[0].Cells) 123 names := getStrings(rows[1].Cells) 124 cols := getColumn(types, names) 125 t.columns = cols 126 for i := start; i<len(rows); i++ { 127 row := getRow(cols, getStrings(rows[i].Cells)) 128 if row != nil { 129 t.rows = append(t.rows, row) 130 } 131 } 132 return t 133 } 134 135 func toLuaTable(tableName string, t *table) string { 136 lines := "g_" + tableName + " = {\n" 137 if t!=nil { 138 for id, row := range t.rows { 139 lines = lines + "\t[" + strconv.Itoa(id+1) + "] = {\n" 140 for k, v := range row { 141 if t.columns[k].name != valid { 142 line := "\t\t" + t.columns[k].name + " = " 143 if t.columns[k].type_ == "s" { 144 line = line + "\"" + v + "\",\n" 145 }else if t.columns[k].type_ == "b" { 146 v = strings.ToLower(v) 147 log.Info("k:%d, v:%s", k, v) 148 if v == "true" || v == "1" { 149 v = "true" 150 }else { 151 v = "false" 152 } 153 line = line + v + ",\n" 154 }else{ 155 _, err := strconv.ParseFloat(v, 32) 156 if err != nil { 157 v = "nil" 158 } 159 line = line + v + ",\n" 160 } 161 lines = lines + line 162 } 163 } 164 lines = lines + "\t},\n" 165 } 166 } 167 lines = lines + "}" 168 return lines 169 } 170 171 func main() { 172 log.SetFilename("excel") 173 excelFileName := os.Args[1] 174 start, e := strconv.Atoi(os.Args[2]) 175 if e!=nil { 176 log.Error(e.Error()) 177 return 178 } 179 xlFile, err := xlsx.OpenFile(excelFileName) 180 if err != nil { 181 log.Error(err.Error()) 182 return 183 } 184 for _, sheet := range xlFile.Sheets { 185 fileName := sheet.Name + ".tmp.lua" 186 file, err := os.Create(fileName) 187 if err != nil { 188 log.Error(err.Error()) 189 return 190 } 191 t := getTable(start-1, sheet.Rows) 192 luaStr := toLuaTable(sheet.Name, t) 193 file.WriteString(luaStr) 194 } 195 }
其中日志库是自己写的flex/log。