我们在实际游戏开发中可能会遇到需要对于读取Excel表格的需求,可以自己开发一套使用二进制读写的插件。整体思路分为两步:

第一步:读取Excel文件

1.将Excel表中的信息生成为结构类,为此我们需要为Excel表格制定一个读写规则。例如下面这张表。第一行为字段名,第二行为字段类型,第三行为主键表示,第四行为解释。从第五行开始往后都是实际需要持久化的数据。

2.制定了Excel表的规则后,我们就开始要使用代码去生成数据结构类和存储结构类的容器(字典),先声明一个ToolExcel类文件和三个路径,如下:

    /// <summary>/// excel文件存放的路径/// </summary>public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";/// <summary>/// 数据结构类脚本存储位置路径/// </summary>public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";    /// <summary>/// 容器类脚本存储位置路径/// </summary>public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/Container/";

3.写一个Unity编辑器内能够调用到的方法,为此方法前应该添加特性,让Unity编辑器能够在外面通过选择框调用到。然后方法内打开表格文件的指定路径,得到其中的所有文件,for循环判断是否为excel表格文件,若不是表格文件直接进入下一次循环,若是表格文件则使用流文件流将文件中所有的表都获取到,然后使用foreach遍历表中所有的信息进行三个操作,代码如下。

[MenuItem("GameTool/GenerateExcel")]private static void GenerateExcelInfo(){//记在指定路径中的所有Excel文件 用于生成对应的3个文件DirectoryInfo dInfo = Directory.CreateDirectory(EXCEL_PATH);//得到指定路径中的所有文件信息 相当于就是得到所有的Excel表FileInfo[] files = dInfo.GetFiles();//数据表容器DataTableCollection tableConllection;for (int i = 0; i < files.Length; i++){//如果不是excel文件就不要处理了if (files[i].Extension != ".xlsx" &&files[i].Extension != ".xls")continue;//打开一个Excel文件得到其中的所有表的数据using (FileStream fs = files[i].Open(FileMode.Open, FileAccess.Read)){IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);tableConllection = excelReader.AsDataSet().Tables;fs.Close();}//遍历文件中的所有表的信息foreach (DataTable table in tableConllection){//生成数据结构类GenerateExcelDataClass(table);//生成容器类GenerateExcelContainer(table);//生成2进制数据GenerateExcelBinary(table);}}

遍历中的三个方法:

1.生成数据结构类GenerateExcelDataClass方法

但是在我们去生成数据结构类之前需要得到表中的一些信息以及我们需要的动态生成的数据结构类是什么样子的。在我们的表格文件例子中,制定了一套规则。(前四行为变量名、类型/主键标识和解释说明)由此可以大概得知具体的类一应该是如下样子。

public class Sheet1
{public int id;public string name;public float atk;public float def;
}

那么在GenerateExcelDataClass方法中,我们就需要去知道数据的类字段名,以及数据类型以便于我们进行字符串拼接为此可以写两个方法GetVariableNameRowGetVariableTypeRow代码如下:

    /// <summary>/// 获取变量名所在行/// </summary>/// <param name="table"></param>/// <returns></returns>private static DataRow GetVariableNameRow(DataTable table){return table.Rows[0];}/// <summary>/// 获取变量类型所在行/// </summary>/// <param name="table"></param>/// <returns></returns>private static DataRow GetVariableTypeRow(DataTable table){return table.Rows[1];}

有了这两个方法之后就可以在GenerateExcelDataClass中去调用得到表的信息,代码如下。其本质其实就是得到后遍历两个DataRow对象,然后运用字符串拼接的方式去生成类。

    /// <summary>/// 生成Excel表对应的数据结构类/// </summary>/// <param name="table"></param>private static void GenerateExcelDataClass(DataTable table){//字段名行DataRow rowName = GetVariableNameRow(table);//字段类型行DataRow rowType = GetVariableTypeRow(table);//判断路径是否存在 没有的话 就创建文件夹if (!Directory.Exists(DATA_CLASS_PATH))Directory.CreateDirectory(DATA_CLASS_PATH);//如果我们要生成对应的数据结构类脚本 其实就是通过代码进行字符串拼接 然后存进文件就行了string str = "public class " + table.TableName + "\n{\n";//变量进行字符串拼接for (int i = 0; i < table.Columns.Count; i++){str += "    public " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";}str += "}";//把拼接好的字符串存到指定文件中去File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", str);//刷新Project窗口AssetDatabase.Refresh();}

同样除了数据结构类以外,我们还需要一个数据容器类

2.生成数据容器类GenerateExcelContainer方法

数据容器类其本质其实就是用来存一行行数据的一个类,其中只有为一个一个字典变量。字典的键就是表中数据的key,字典的值就是数据结构类,用来存放一行行的数据。

public class Sheet1Container
{public Dictionary<int,Sheet1>dataDic = new Dictionary<int, Sheet1>();
}

为此我们需要一个方法GetKeyIndex,由于表格制定了规则(第三行表格标记出主键),所以我们需要知道主键的索引值,以便于确定是第三行的哪一列为索引。代码如下:

        private static int GetKeyIndex(DataTable table) { DataRow row = table.Rows[2];for (int i = 0; i < table.Columns.Count; i++){if (row[i].ToString() == "key")return i;}return 0;}

由此获取到了key的所在列后就可以知道是哪一个数据被作为了key,将这个被当作主键的数据代入函数中,代码如下:

    /// <summary>/// 生成Excel表对应的数据容器类/// </summary>/// <param name="table"></param>private static void GenerateExcelCotainer(DataTable table) {int keyIndex = GetKeyIndex(table);DataRow rowType = GetVariableTypeRow(table);if (!Directory.Exists(BinaryDataMgr.DATA_CONTAINER_PATH))Directory.CreateDirectory(BinaryDataMgr.DATA_CONTAINER_PATH);string str = "using System.Collections.Generic;\n";str += "public class " + table.TableName + "Container" + "\n{\n";str += "    ";str += "public Dictionary<" + rowType[keyIndex].ToString() + "," + table.TableName + ">";str += "dataDic = new " + "Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">();\n";str += "}";File.WriteAllText(BinaryDataMgr.DATA_CONTAINER_PATH+"/"+ table.TableName + "Container.cs",str);AssetDatabase.Refresh();}

在得到了两个数据类之后就可以开始生成二进制文件了

3.生成二进制文件GenerateExcelBinary(DataTable tabel)   传入参数为一个表

该方法其实就是先判断是否存在用于存放二进制文件的路径,如果不存在则创建,然后开辟一条二进制文件数据流。依次写入传入的表的数据信息。

1.先写入需要存储多少行数据(由于我们Excel表格制定的规则是前四行为表格信息,所以数据是从第五第五行开始)

2.写入主键的变量名(有现成的方法去获取主键的Index)只需要去获取变量名的那会一行数据的第Index个数据然后转成字符串

3.遍历所有的内容的行,然后根据类型写入(写入是一行行数据写入的。先根据行数遍历出所有行,然后再根据类型行的长度去一列列判断类型写入文件流中)

4.最后关闭流

    /// <summary>/// 生成Excel二进制数据/// </summary>/// <param name="table"></param>private static void GenerateExcelBinary(DataTable table){if (Directory.Exists(BinaryDataMgr.DATA_CONTAINER_PATH))Directory.CreateDirectory(BinaryDataMgr.DATA_CONTAINER_PATH);//创建一个二进制文件写入using (FileStream fs = new FileStream(BinaryDataMgr.DATA_CONTAINER_PATH + table.TableName + ".mqx", FileMode.OpenOrCreate, FileAccess.Write)) {//存储具体的excel对应的二进制信息//1.先存储我们需要写入多少行的数据 方便读取fs.Write(BitConverter.GetBytes(table.Rows.Count - 4),0,4);//2.存储主键的变量名string keyName = GetVariableNameRow(table)[GetKeyIndex(table)].ToString();byte[] bytes = Encoding.UTF8.GetBytes(keyName);//存储字符串字节数组长度fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4);//存储字符串字节数组fs.Write(bytes, 0, bytes.Length);//3.遍历所有内容的行 进行二进制的写入DataRow row;//得到类型行 根据规则决定如何写入书DataRow rowType = GetVariableTypeRow(table);for (int i = BINGEN_INDEX; i < table.Rows.Count; i++){row = table.Rows[i];for (int j = 0; j < table.Columns.Count; j++){switch (rowType[j].ToString()){case "int":fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())),0,4);break;case "float":fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())), 0,4);break;case "sting":fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())), 0, 1);break;case "bool":bytes = Encoding.UTF8.GetBytes(row[j].ToString());//写入字符串字节数组长度fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4);//写入字符串字节数组fs.Write(bytes, 0, bytes.Length);break;}}}fs.Close();}AssetDatabase.Refresh();}

由上我们以及完成了对于Excel表个的读取和写入二进制文件。

但是我们将Excel表格存成了二级制后,需要将它读出来存放到内存中去。为此我们还需要一个类BinaryDataManager用于去读二进制文件,由于是Manager管理器类,可以写成单例模式。为此我们可以先写一个单例模式。代码如下:

一个单例接口,一个用于初始化的单例抽象。然后在类中需要的有三个字段,其中一个字段DATA_BINARY_PATH(二进制数据的存储路径)会被ToolExcel给用到。还有一个用于存储从硬盘获取到内存的字典容器。

public interface ISingleton {void Init();
}public abstract class Singleton<T> where T : ISingleton, new() {private static T instance;public static T Instance {get {if (instance==null){instance = new Lazy<T>(true).Value;instance.Init();}return instance;}}
}/// <summary>
/// 2进制数据管理器
/// </summary>
public class BinaryDataMgr:ISingleton
{
/// <summary>/// 2进制数据存储位置路径/// </summary>public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Binary/";/// <summary>/// 用于存储所有Excel表数据的容器/// </summary>private Dictionary<string, object> tableDic = new Dictionary<string, object>();/// <summary>/// 数据存储的位置/// </summary>private static string SAVE_PATH = Application.persistentDataPath + "/Data/";public static BinaryDataMgr Instace => Singleton<BinaryDataMgr>.Instance;
}

其中还需一个方法 LoadTable<T,K> 两个泛型参数分别是容器类名和数据结构类类名,方法代码如下:

我们可以把这个方法分为几个部分

1.开流读文件,把流中的字节码存储下来然后关闭流

2.读取流中存储的特性信息,如读了应该读取多少行、主键的名字

3.反射创建容器类对象

4.拿到数据结构类的Type然后反射获取到其中所有的信息

5.读取每一行信息,通过反射实例化一个数据结构对象,然后遍历数据结构信息对象(infos)判断其中的数据类型,从字节数组中读取对应类型

由于写入二进制文件的顺序是按照数据结构类的类型顺序写入的,所以按照顺序一行行读取不会出现问题。

6.得到容器对象中的字典对象,然后反射条用字典的Add方法,然后得到键的值使用反射调用方法最后把读取完的表记录下来。

7.关闭流

    public void LoadTable<T,K>(){//读取 excel表对应的2进制文件 来进行解析using (FileStream fs = File.Open(DATA_BINARY_PATH + typeof(K).Name + ".tang", FileMode.Open, FileAccess.Read)){byte[] bytes = new byte[fs.Length];fs.Read(bytes, 0, bytes.Length);fs.Close();//用于记录当前读取了多少字节了int index = 0;//读取多少行数据int count = BitConverter.ToInt32(bytes, index);index += 4;//读取主键的名字int keyNameLength = BitConverter.ToInt32(bytes, index);index += 4;string keyName = Encoding.UTF8.GetString(bytes, index, keyNameLength);index += keyNameLength;//创建容器类对象Type contaninerType = typeof(T);object contaninerObj = Activator.CreateInstance(contaninerType);//得到数据结构类的TypeType classType = typeof(K);//通过反射 得到数据结构类 所有字段的信息FieldInfo[] infos = classType.GetFields();//读取每一行的信息for (int i = 0; i < count; i++){//实例化一个数据结构类 对象object dataObj = Activator.CreateInstance(classType);foreach (FieldInfo info in infos){if( info.FieldType == typeof(int) ){//相当于就是把2进制数据转为int 然后赋值给了对应的字段info.SetValue(dataObj, BitConverter.ToInt32(bytes, index));index += 4;}else if (info.FieldType == typeof(float)){info.SetValue(dataObj, BitConverter.ToSingle(bytes, index));index += 4;}else if (info.FieldType == typeof(bool)){info.SetValue(dataObj, BitConverter.ToBoolean(bytes, index));index += 1;}else if (info.FieldType == typeof(string)){//读取字符串字节数组的长度int length = BitConverter.ToInt32(bytes, index);index += 4;info.SetValue(dataObj, Encoding.UTF8.GetString(bytes, index, length));index += length;}}//读取完一行的数据了 应该把这个数据添加到容器对象中//得到容器对象中的 字典对象object dicObject = contaninerType.GetField("dataDic").GetValue(contaninerObj);//通过字典对象得到其中的 Add方法MethodInfo mInfo = dicObject.GetType().GetMethod("Add");//得到数据结构类对象中 指定主键字段的值object keyValue = classType.GetField(keyName).GetValue(dataObj);mInfo.Invoke(dicObject, new object[] { keyValue, dataObj });}//把读取完的表记录下来tableDic.Add(typeof(T).Name, contaninerObj);fs.Close();}}

在外部声明一个调用该方法的函数,将生成的数据类结构和数据容器的类型名作为泛型方法传去上述函数即可在内存中得到硬盘中存储的二进制数据。

完成了编辑器的实现。

Unity开发Excel表格读取器相关推荐

  1. Unity 之 Excel表格转换为Unity用的文件格式 -- ScriptableObject,Json,XML 全部搞定

    Unity 之 Excel表格转换为Unity用的文件格式 -- ScriptableObject,Json,XML 全部搞定 前言 一,准备工作 1.1 确认表格表头 1.2 读取Excel 1.3 ...

  2. Py自动化办公—Word文档替换、Excel表格读取、Pdf文件生成和Email自动邮件发送实战案例...

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 平阳歌舞新承宠,帘外春寒赐锦袍. ...

  3. 使用最新的poi-4.1.0.jar导入导出Excel表格——读取Excel表格数据用法

    使用最新的poi-4.1.0.jar导入导出Excel表格--读取Excel表格数据用法 其中主要的一点心得就是在switch语句哪里进行读取数据转换时,我看到网上的一些用法都是使用 HSSFCell ...

  4. Unity游戏开发——Excel数据读取到游戏中

    C# Unity游戏开发--Excel中的数据是如何到游戏中的 (一) 引言 现在做游戏开发的没有几个不用Excel的,用的最多的就是策划.尤其是数值策划,Excel为用户提供强大的工具,各种快捷键, ...

  5. 从excel表格读取日期利用python简单实现农历转阳历功能(1901-2099年之间)

    从表格E列里读取出阳历或者农历日期数据,全部转变为阳历日期写入到F列,如果是农历日期通过下面网站进行查询后,将阳历结果写进相应农历日期对应的表格中,样式如下: 网站:https://gonglinon ...

  6. 使用EasyExcel从Excel表格读取链接地址下载文件

    使用easyExcel读取excel表格,再读取excel表格中的链接地址下载文件到本地. 1.引入easyexcel依赖 <dependency><groupId>com.a ...

  7. python处理Excel表格--读取Excel表格

    文章目录 前言 一.读取Excel表格 二.项目:2010年美国人口普查数据自动化处理 2.1 做什么.如何做? 2.2 print()与pprint() 2.3 完整源码及分析 前言 python处 ...

  8. Unity 生成 Excel表格

    Unity生成Excel需要EPPlus这个库,Editor状态可以使用,打包后需要加入 I18N.CJK  和 I18N 这两个库 代码如下: private void SaveExcel(){Op ...

  9. python通过Excel表格读取存储数据

    库:openpyxl 内容包含: 1.读取Excel表格内容 2.将列表写入Excel的某一列 3.将列表写入Excel的某一行 话不多说,直接看代码: from openpyxl import lo ...

最新文章

  1. 下列不是python数据类型的是_Python数据类型方法精心整理,不必死记硬背,看看源码一切都有了...
  2. 时间排序python_算法导论 第八章 线性时间排序(python)
  3. Eclipse中的常用快捷键
  4. Linux通常把设备对象抽象为,linux 设备模型(1)
  5. JS实现刷新iframe的方法
  6. 项目经理问我Java内存区域模型!急急急
  7. jcmd 命令_jcmd:一个可以全部统治的JDK命令行工具
  8. Win7的电脑屏幕如何优化设置
  9. Linux pwn入门教程,i春秋linux_pwn入门教程复现之栈溢出基础
  10. [Redis]c# redis缓存辅助类
  11. Win10如何为命令提示符增加输入命令获取管理员权限功能?
  12. 指南针经纬度分秒格式转换10进制经纬度
  13. 水星怎么设置网速最快_水星怎么设置路由器的网速
  14. VoIP技术的基本原理与应用
  15. 后端学习 - 操作系统
  16. 字符编码的常用种类介绍
  17. skt7850鸿蒙策略,SKT九连跪,牛肉面
  18. 运动学模型(二)----轮速计 后轮速差模型
  19. 《匆匆那年》的你,还记得吗?数学中的那些有(hui)趣(se)的定理(1)——鸟头定理
  20. \0 的ASCII码值是多少

热门文章

  1. Elliptic Curve Cryptography: 轻轻的学
  2. 基于 mPaaS 框架 Portal-Bundle 接入方式下 Multidex 分包失效的解决方法
  3. 残疾人竞赛计算机程序,第五届全国残疾人职业技能竞赛竞赛标准计算机程序.doc...
  4. 18c和12客户端 oracle,客户端连接 12、18c 报ORA-28040和ORA-01017 的解决方法
  5. C++基础之运算符重载
  6. 创业起步阶段需要注意的几点
  7. 我读《高效能人士的七个习惯》
  8. 面试官问你的职业生涯规划是什么,该如何回答?
  9. 中国石油大学《混凝土》第二阶段在线作业
  10. 常用的excel公式备忘