• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

【游戏开发】Excel表格批量转换成lua的转表工具

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

一、简介

  在上篇博客《【游戏开发】Excel表格批量转换成CSV的小工具》 中,我们介绍了如何将策划提供的Excel表格转换为轻便的CSV文件供开发人员使用。实际在Unity开发中,很多游戏都是使用Lua语言进行开发的。如果要用Lua直接读取CSV文件的话,又要写个对应的CSV解析类,不方便的同时还会影响一些加载速度,牺牲游戏性能。因此我们可以直接将Excel表格转换为lua文件,这样就可以高效、方便地在Lua中使用策划配置的数据了。在本篇博客中,马三将会和大家一起,用C#语言实现一个Excel表格转lua的转表工具——Xls2Lua,并搭配一个通用的ConfigMgr来读取lua配置文件。

二、开发环境准备

  由于要使用C#来读取Excel表格文件,所以我们需要使用一些第三方库。针对C#语言,比较好用的Excel库有NPOI和CSharpJExcel 这两个,其实无论哪个库都是可以用的,我们只是用它来读取Excel表格中的数据罢了。马三在本篇博客中使用的是CSharpJExcel库,因为它相对来说更轻便一些。下面附上NPOI和CSharpJExcel库的下载链接:

三、转表工具

1.思路分析

  一切准备就绪,可以开始我们的开发任务了。首先我们来大致地说一下转表工具的思路:

  1. 读取Excel表格文件的数据,依次读取配置目录下的Excel文件,然后逐个读取表里面Sheet的内容;
  2. 根据Excel表格中配置的字段类型,对数据进行校验,判断数据是否合法;
  3. 将通过校验的数据转为lua文件,一个Sheet切页对应一个lua配置文件;
  4. 使用通用的ConfigMgr对转出来的lua配置文件进行读取操作;

2.目录结构

  项目整体的目录结构如下图所示:

  

  图1:转表工具整体目录结构

  ConfigMgr存放我们的ConfigMgr.lua,它是一个工具类,用来读取并管理转出来的Lua配置文件,兼具缓存数据的功能。Excel目录存放我们需要进行转换的Excel表格文件。LuaData目录存放转出来的Lua配置文件。Xls2Lua目录也就是我们的转表工具的目录了,它包含源代码和可直接运行的转表工具。

  转表工具的设计结构如下图所示:

  

  图2:转表工具设计结构

  FileExporter类专门用来读取Excel文件和导出lua配置文件;GlobalDef类中定义了一些通用的数据结构和枚举等信息;XlsTransfer类即为我们的转表工具核心类,大部分数据都是在这里进行校验处理的。

  下面我们就可以按照之前分析出来的思路编写具体的代码了,首先放上来的是我们主程序的入口,我们有一个名为config.ini的配置文件,程序运行的时候会先去这个配置信息中读取Excel的目录和输出目录,然后调用FileExporter.ExportAllLuaFile函数进行转表操作。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Xls2Lua
 9 {
10     class Program
11     {
12         private static string inDir;
13         private static string outDir;
14         private static readonly string configPath = "./config.ini";
15 
16         static void Main(string[] args)
17         {
18             ReadConfig();
19             FileExporter.ExportAllLuaFile(inDir, outDir);
20         }
21 
22         private static void ReadConfig()
23         {
24             StreamReader reader = new StreamReader(configPath, Encoding.UTF8);
25             inDir = reader.ReadLine().Split(',')[1];
26             inDir = Path.GetFullPath(inDir);
27             outDir = reader.ReadLine().Split(',')[1];
28             outDir = Path.GetFullPath(outDir);
29             reader.Close();
30         }
31     }
32 }
View Code

  下面是FileExporter.cs的代码,在这里我们用到了之前提及的CSharpJExcel库,我们需要先把它加到我们工程的引用项中,然后在代码里调用即可。在这部分代码中,我们首先会调用ClearDirectory函数,清空之前转出来的lua配置文件。然后遍历Excel目录下的所有Excel文件,对其依次执行ExportSingleLuaFile函数。在ExportSingleLuaFile函数中主要做的是打开每一张Excel表格,并且依次遍历里面的Sheet文件,对其中命名合法的Sheet切页进行导出(sheet名称前带有#的为导出的表格,不带#的会被自动忽略掉,通过这个规则可以方便自由地控制导出规则,决定哪些Sheet导出,哪些Sheet不导出)。

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading.Tasks;
  7 using CSharpJExcel.Jxl;
  8 
  9 namespace Xls2Lua
 10 {
 11     /// <summary>
 12     /// 负责最终文件的输出保存等操作类
 13     /// </summary>
 14     public class FileExporter
 15     {
 16 
 17         /// <summary>
 18         /// 清空某个DIR下的内容
 19         /// </summary>
 20         /// <param name="dir"></param>
 21         public static void ClearDirectory(string dir)
 22         {
 23             if (!Directory.Exists(dir))
 24             {
 25                 return;
 26             }
 27             Console.WriteLine("清空目录:" + dir);
 28             DirectoryInfo directoryInfo = new DirectoryInfo(dir);
 29             FileSystemInfo[] fileSystemInfos = directoryInfo.GetFileSystemInfos();
 30 
 31             foreach (var info in fileSystemInfos)
 32             {
 33                 if (info is DirectoryInfo)
 34                 {
 35                     DirectoryInfo subDir = new DirectoryInfo(info.FullName);
 36                     try
 37                     {
 38                         subDir.Delete(true);
 39                     }
 40                     catch (Exception e)
 41                     {
 42                         Console.WriteLine("警告:目录删除失败 " + e.Message);
 43                     }
 44                 }
 45                 else
 46                 {
 47                     try
 48                     {
 49                         File.Delete(info.FullName);
 50                     }
 51                     catch (Exception e)
 52                     {
 53                         Console.WriteLine("警告:文件删除失败 " + e.Message);
 54                     }
 55                 }
 56             }
 57         }
 58 
 59         /// <summary>
 60         /// 导出所有的Excel配置到对应的lua文件中
 61         /// </summary>
 62         /// <param name="inDir"></param>
 63         /// <param name="outDir"></param>
 64         public static void ExportAllLuaFile(string inDir, string outDir)
 65         {
 66             ClearDirectory(outDir);
 67             List<string> allXlsList = Directory.GetFiles(inDir, "*.xls", SearchOption.AllDirectories).ToList();
 68             Console.WriteLine("开始转表...");
 69             foreach (var curXlsName in allXlsList)
 70             {
 71                 ExportSingleLuaFile(curXlsName, outDir);
 72             }
 73             Console.WriteLine("按任意键继续...");
 74             Console.ReadKey();
 75         }
 76 
 77         public static void ExportSingleLuaFile(string xlsName, string outDir)
 78         {
 79             if (".xls" != Path.GetExtension(xlsName).ToLower())
 80             {
 81                 return;
 82             }
 83 
 84             Console.WriteLine(Path.GetFileName(xlsName));
 85 
 86             //打开文件流
 87             FileStream fs = null;
 88             try
 89             {
 90                 fs = File.Open(xlsName, FileMode.Open);
 91             }
 92             catch (Exception e)
 93             {
 94                 Console.WriteLine(e.Message);
 95                 throw;
 96             }
 97             if (null == fs) return;
 98             //读取xls文件
 99             Workbook book = Workbook.getWorkbook(fs);
100             fs.Close();
101             //循环处理sheet
102             foreach (var sheet in book.getSheets())
103             {
104                 string sheetName = XlsTransfer.GetSheetName(sheet);
105                 if (string.IsNullOrEmpty(sheetName)) continue;
106                 sheetName = sheetName.Substring(1, sheetName.Length - 1);
107                 Console.WriteLine("Sheet:" + sheetName);
108                 string outPath = Path.Combine(outDir, sheetName + ".lua");
109                 string content = XlsTransfer.GenLuaFile(sheet);
110                 if (!string.IsNullOrEmpty(content))
111                 {
112                     File.WriteAllText(outPath, content);
113                 }
114             }
115         }
116     }
117 }
View Code

   下面是GloablDef.cs的代码,我们主要在里面定义了一些字段类型的枚举和一些通用数据结构,其中的ColoumnDesc类用来存储表格数据:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Xls2Lua
 8 {
 9     /// <summary>
10     /// 表格字段类型的枚举
11     /// </summary>
12     public enum FieldType : byte
13     {
14         c_unknown,
15         c_int32,
16         c_int64,
17         c_bool,
18         c_float,
19         c_double,
20         c_string,
21         c_uint32,
22         c_uint64,
23         c_fixed32,
24         c_fixed64,
25         c_enum,
26         c_struct
27     }
28 
29     /// <summary>
30     /// 表头字段描述
31     /// </summary>
32     public class ColoumnDesc
33     {
34         public int index = -1;
35         public string comment = "";
36         public string typeStr = "";
37         public string name = "";
38         public FieldType type;
39         public bool isArray = false;
40     }
41 }
View Code

   最后压轴出场的是我们的核心类:XlsTransfer,其核心代码如下: 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using CSharpJExcel.Jxl;
  7 
  8 namespace Xls2Lua
  9 {
 10 
 11     /// <summary>
 12     /// Xls表格转换处理核心类
 13     /// </summary>
 14     public class XlsTransfer
 15     {
 16         /// <summary>
 17         /// 分割字符串的依据
 18         /// </summary>
 19         private static readonly char[] splitSymbol = { '|' };
 20 
 21         /// <summary>
 22         /// 根据字符串返回对应字段类型
 23         /// </summary>
 24         /// <param name="str"></param>
 25         /// <returns></returns>
 26         public static FieldType StringToFieldType(string str)
 27         {
 28             str = str.Trim();
 29             str = str.ToLower();
 30             if ("int32" == str)
 31                 return FieldType.c_int32;
 32             else if ("int64" == str)
 33                 return FieldType.c_int64;
 34             else if ("bool" == str)
 35                 return FieldType.c_bool;
 36             else if ("float" == str)
 37                 return FieldType.c_float;
 38             else if ("double" == str)
 39                 return FieldType.c_double;
 40             else if ("string" == str)
 41                 return FieldType.c_string;
 42             else if ("uint32" == str)
 43                 return FieldType.c_uint32;
 44             else if ("uint64" == str)
 45                 return FieldType.c_uint64;
 46             else if ("fixed32" == str)
 47                 return FieldType.c_fixed32;
 48             else if ("fixed64" == str)
 49                 return FieldType.c_fixed64;
 50             return FieldType.c_unknown;
 51         }
 52 
 53         /// <summary>
 54         /// 根据字段类型,返回对应的字符串
 55         /// </summary>
 56         /// <param name="type"></param>
 57         /// <returns></returns>
 58         public static string FieldTypeToString(FieldType type)
 59         {
 60             if (type == FieldType.c_int32)
 61             {
 62                 return "int32";
 63             }
 64             else if (type == FieldType.c_int64)
 65             {
 66                 return "int64";
 67             }
 68             else if (type == FieldType.c_bool)
 69             {
 70                 return "bool";
 71             }
 72             else if (type == FieldType.c_float)
 73             {
 74                 return "float";
 75             }
 76             else if (type == FieldType.c_double)
 77             {
 78                 return "double";
 79             }
 80             else if (type == FieldType.c_string)
 81             {
 82                 return "string";
 83             }
 84             else if (type == FieldType.c_uint32)
 85             {
 86                 return "uint32";
 87             }
 88             else if (type == FieldType.c_uint64)
 89             {
 90                 return "uint64";
 91             }
 92             else if (type == FieldType.c_fixed32)
 93             {
 94                 return "fixed32";
 95             }
 96             else if (type == FieldType.c_fixed64)
 97             {
 98                 return "fixed64";
 99             }
100             return "";
101         }
102 
103         /// <summary>
104         /// 获取表格的列数,表头碰到空白列直接中断
105         /// </summary>
106         public static int GetSheetColoumns(Sheet sheet)
107         {
108             int coloum = sheet.getColumns();
109             for (int i = 0; i < coloum; i++)
110             {
111                 string temp1 = sheet.getCell(i, 1).getContents();
112                 string temp2 = sheet.getCell(i, 2).getContents();
113                 if (string.IsNullOrWhiteSpace(temp1) || string.IsNullOrWhiteSpace(temp2))
114                 {
115                     return i;
116                 }
117             }
118             return coloum;
119         }
120 
121         /// <summary>
122         /// 获取表格行数,行开头是空白直接中断
123         /// </summary>
124         /// <param name="sheet"></param>
125         /// <returns></returns>
126         public static int GetSheetRows(Sheet sheet)
127         {
128             int rows = sheet.getRows();
129             for (int i = 0; i < sheet.getRows(); i++)
130             {
131                 if (i >= 5)
132                 {
133                     if (string.IsNullOrEmpty(sheet.getCell(0, i).getContents()))
134                     {
135                         return i;
136                     }
137                 }
138             }
139             return rows;
140         }
141 
142         /// <summary>
143         /// 获取当前Sheet切页的表头信息
144         /// </summary>
145         /// <param name="sheet"></param>
146         /// <returns></returns>
147         public static List<ColoumnDesc> GetColoumnDesc(Sheet sheet)
148         {
149             int coloumnCount = GetSheetColoumns(sheet);
150             List<ColoumnDesc> coloumnDescList = new List<ColoumnDesc>();
151             for (int i = 0; i < coloumnCount; i++)
152             {
153                 string comment = sheet.getCell(i, 0).getContents().Trim();
154                 comment = string.IsNullOrWhiteSpace(comment) ? comment : comment.Split('\n')[0];
155                 string typeStr = sheet.getCell(i, 1).getContents().Trim();
156                 string nameStr = sheet.getCell(i, 2).getContents().Trim();
157 
158                 bool isArray = typeStr.Contains("[]");
159                 typeStr = typeStr.Replace("[]", "");
160                 FieldType fieldType;
161                 if (typeStr.ToLower().StartsWith("struct-"))
162                 {
163                     typeStr = typeStr.Remove(0, 7);
164                     fieldType = FieldType.c_struct;
165                 }
166                 else if (typeStr.ToLower().StartsWith("enum-"))
167                 {
168                     typeStr.Remove(0, 5);
169                     fieldType = FieldType.c_enum;
170                 }
171                 else
172                 {
173                     fieldType = StringToFieldType(typeStr);
174                 }
175                 ColoumnDesc coloumnDesc = new ColoumnDesc();
176                 coloumnDesc.index = i;
177                 coloumnDesc.comment = comment;
178                 coloumnDesc.typeStr = typeStr;
179                 coloumnDesc.name = nameStr;
180                 coloumnDesc.type = fieldType;
181                 coloumnDesc.isArray = isArray;
182                 coloumnDescList.Add(coloumnDesc);
183             }
184             return coloumnDescList;
185         }
186 
187         /// <summary>
188         /// 生成最后的lua文件
189         /// </summary>
190         /// <param name="coloumnDesc"></param>
191         /// <param name="sheet"></param>
192         /// <returns></returns>
193         public static string GenLuaFile(Sheet sheet)
194         {
195             List<ColoumnDesc> coloumnDesc = GetColoumnDesc(sheet);
196 
197             StringBuilder stringBuilder = new StringBuilder();
198             stringBuilder.Append("--[[Notice:This lua config file is auto generate by Xls2Lua Tools,don't modify it manually! --]]\n");
199             if (null == coloumnDesc || coloumnDesc.Count <= 0)
200             {
201                 return stringBuilder.ToString();
202             }
203             //创建索引
204             Dictionary<string, int> fieldIndexMap = new Dictionary<string, int>();
205             for (int i = 0; i < coloumnDesc.Count; i++)
206             {
207                 fieldIndexMap[coloumnDesc[i].name] = i + 1;
208             }
209             //创建数据块的索引表
210             stringBuilder.Append("local fieldIdx = {}\n");
211             foreach ( 
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
3-学习GPRS_Air202(需要知道的关于Lua的一些基本的知识)发布时间:2022-07-22
下一篇:
Bypass ngx_lua_waf SQL注入防御(多姿势)发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap