ExcelDataReader.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #if UNITY_EDITOR
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using System.IO;
  6. using Excel;
  7. using System.Reflection;
  8. using System;
  9. //Excel中间数据
  10. public class ExcelMediumData
  11. {
  12. //Excel名字
  13. public string excelName;
  14. //Dictionary<字段名称, 字段类型>,记录类的所有字段及其类型
  15. public Dictionary<string, string> propertyNameTypeDic;
  16. //List<一行数据>,List<Dictionary<字段名称, 一行的每个单元格字段值>>
  17. //记录类的所有字段值,按行记录
  18. public List<Dictionary<string, string>> allItemValueRowList;
  19. }
  20. public static class ExcelDataReader
  21. {
  22. //Excel第2行对应字段名称
  23. const int excelNameRow = 2;
  24. //Excel第4行对应字段类型
  25. const int excelTypeRow = 4;
  26. //Excel第5行及以后对应字段值
  27. const int excelDataRow = 5;
  28. //Excel读取路径
  29. public static string excelFilePath = Application.dataPath + "/Tools/module_excel/Excel";
  30. // public static string excelFilePath = Application.dataPath.Replace("Assets", "Excel");
  31. //自动生成C#类文件路径
  32. static string excelCodePath = Application.dataPath + "/Tools/module_excel/Script/Excel/AutoCreateCSCode";
  33. //自动生成Asset文件路径
  34. static string excelAssetPath = "Assets/Tools/module_excel/Resources/ExcelAsset";
  35. //static string excelAssetPath = "Assets/Resources/ClimbHigh";
  36. #region --- Read Excel ---
  37. //创建Excel对应的C#类
  38. public static void ReadAllExcelToCode()
  39. {
  40. //读取所有Excel文件
  41. //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
  42. string[] excelFileFullPaths = Directory.GetFiles(excelFilePath, "*.xlsx");
  43. if (excelFileFullPaths == null || excelFileFullPaths.Length == 0)
  44. {
  45. Debug.Log("Excel file count == 0");
  46. return;
  47. }
  48. //遍历所有Excel,创建C#类
  49. for (int i = 0; i < excelFileFullPaths.Length; i++)
  50. {
  51. ReadOneExcelToCode(excelFileFullPaths[i]);
  52. }
  53. }
  54. //创建Excel对应的C#类
  55. public static void ReadOneExcelToCode(string excelFileFullPath)
  56. {
  57. //解析Excel获取中间数据
  58. ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFileFullPath);
  59. if (excelMediumData != null)
  60. {
  61. //根据数据生成C#脚本
  62. string classCodeStr = ExcelCodeCreater.CreateCodeStrByExcelData(excelMediumData);
  63. if (!string.IsNullOrEmpty(classCodeStr))
  64. {
  65. //写文件,生成CSharp.cs
  66. if (WriteCodeStrToSave(excelCodePath, excelMediumData.excelName + "ExcelData", classCodeStr))
  67. {
  68. Debug.Log("<color=green>Auto Create Excel Scripts Success : </color>" + excelMediumData.excelName);
  69. return;
  70. }
  71. }
  72. }
  73. //生成失败
  74. Debug.LogError("Auto Create Excel Scripts Fail : " + (excelMediumData == null ? "" : excelMediumData.excelName));
  75. }
  76. #endregion
  77. #region --- Create Asset ---
  78. //创建Excel对应的Asset数据文件
  79. public static void CreateAllExcelAsset()
  80. {
  81. //读取所有Excel文件
  82. //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
  83. string[] excelFileFullPaths = Directory.GetFiles(excelFilePath, "*.xlsx");
  84. if (excelFileFullPaths == null || excelFileFullPaths.Length == 0)
  85. {
  86. Debug.Log("Excel file count == 0");
  87. return;
  88. }
  89. //遍历所有Excel,创建Asset
  90. for (int i = 0; i < excelFileFullPaths.Length; i++)
  91. {
  92. CreateOneExcelAsset(excelFileFullPaths[i]);
  93. }
  94. }
  95. //创建Excel对应的Asset数据文件
  96. public static void CreateOneExcelAsset(string excelFileFullPath)
  97. {
  98. //解析Excel获取中间数据
  99. ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFileFullPath);
  100. if (excelMediumData != null)
  101. {
  102. //获取当前程序集
  103. //Assembly assembly = Assembly.GetExecutingAssembly();
  104. //创建类的实例,返回为 object 类型,需要强制类型转换,assembly.CreateInstance("类的完全限定名(即包括命名空间)");
  105. //object class0bj = assembly.CreateInstance(excelMediumData.excelName + "Assignment",true);
  106. //必须遍历所有程序集来获得类型。当前在Assembly-CSharp-Editor中,目标类型在Assembly-CSharp中,不同程序将无法获取类型
  107. Type type = null;
  108. foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
  109. {
  110. //查找目标类型
  111. Type tempType = asm.GetType(excelMediumData.excelName + "AssetAssignment");
  112. if (tempType != null)
  113. {
  114. type = tempType;
  115. break;
  116. }
  117. }
  118. if (type != null)
  119. {
  120. //反射获取方法
  121. MethodInfo methodInfo = type.GetMethod("CreateAsset");
  122. if (methodInfo != null)
  123. {
  124. methodInfo.Invoke(null, new object[] { excelMediumData.allItemValueRowList, excelAssetPath });
  125. //创建Asset文件成功
  126. Debug.Log("<color=green>Auto Create Excel Asset Success : </color>" + excelMediumData.excelName);
  127. return;
  128. }
  129. }
  130. }
  131. //创建Asset文件失败
  132. Debug.LogError("Auto Create Excel Asset Fail : " + (excelMediumData == null ? "" : excelMediumData.excelName));
  133. }
  134. #endregion
  135. #region --- private ---
  136. //解析Excel,创建中间数据
  137. private static ExcelMediumData CreateClassCodeByExcelPath(string excelFileFullPath)
  138. {
  139. if (string.IsNullOrEmpty(excelFileFullPath))
  140. return null;
  141. excelFileFullPath = excelFileFullPath.Replace("\\", "/");
  142. FileStream stream = File.Open(excelFileFullPath, FileMode.Open, FileAccess.Read);
  143. if (stream == null)
  144. return null;
  145. //解析Excel
  146. IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
  147. //无效Excel
  148. if (excelReader == null || !excelReader.IsValid)
  149. {
  150. Debug.Log("Invalid excel : " + excelFileFullPath);
  151. return null;
  152. }
  153. //<数据名称,数据类型>
  154. KeyValuePair<string, string>[] propertyNameTypes = null;
  155. //List<KeyValuePair<数据名称, 单元格数据值>[]>,所有数据值,按行记录
  156. List<Dictionary<string, string>> allItemValueRowList = new List<Dictionary<string, string>>();
  157. //每行数据数量
  158. int propertyCount = 0;
  159. //当前遍历行,从1开始
  160. int curRowIndex = 1;
  161. //Debug.Log(excelReader.FieldCount);
  162. //开始读取,按行遍历
  163. while (excelReader.Read())
  164. {
  165. if (excelReader.FieldCount == 0)
  166. continue;
  167. //读取一行的数据
  168. string[] datas = new string[excelReader.FieldCount];
  169. for (int j = 0; j < excelReader.FieldCount; ++j)
  170. {
  171. //赋值一行的每一个单元格数据
  172. datas[j] = excelReader.GetString(j);
  173. }
  174. //空行/行第一个单元格为空,视为无效数据
  175. if (datas.Length == 0 || string.IsNullOrEmpty(datas[0]))
  176. {
  177. curRowIndex++;
  178. continue;
  179. }
  180. //数据行
  181. if (curRowIndex >= excelDataRow)
  182. {
  183. //数据无效
  184. if (propertyCount <= 0)
  185. return null;
  186. Dictionary<string, string> itemDic = new Dictionary<string, string>(propertyCount);
  187. //遍历一行里的每个单元格数据
  188. for (int j = 0; j < propertyCount; j++)
  189. {
  190. //判断长度
  191. if (j < datas.Length)
  192. {
  193. itemDic[propertyNameTypes[j].Key] = datas[j];
  194. }
  195. else
  196. {
  197. itemDic[propertyNameTypes[j].Key] = null;
  198. }
  199. }
  200. allItemValueRowList.Add(itemDic);
  201. }
  202. //数据名称行
  203. else if (curRowIndex == excelNameRow)
  204. {
  205. //以数据名称确定每行的数据数量
  206. propertyCount = datas.Length;
  207. if (propertyCount <= 0)
  208. return null;
  209. //记录数据名称
  210. propertyNameTypes = new KeyValuePair<string, string>[propertyCount];
  211. for (int i = 0; i < propertyCount; i++)
  212. {
  213. propertyNameTypes[i] = new KeyValuePair<string, string>(datas[i], null);
  214. }
  215. }
  216. //数据类型行
  217. else if (curRowIndex == excelTypeRow)
  218. {
  219. //数据类型数量少于指定数量,数据无效
  220. if (propertyCount <= 0 || datas.Length < propertyCount)
  221. return null;
  222. //记录数据名称及类型
  223. for (int i = 0; i < propertyCount; i++)
  224. {
  225. propertyNameTypes[i] = new KeyValuePair<string, string>(propertyNameTypes[i].Key, datas[i]);
  226. }
  227. }
  228. curRowIndex++;
  229. }
  230. if (propertyNameTypes.Length == 0 || allItemValueRowList.Count == 0)
  231. return null;
  232. ExcelMediumData excelMediumData = new ExcelMediumData();
  233. //类名
  234. excelMediumData.excelName = excelReader.Name;
  235. //Dictionary<数据名称,数据类型>
  236. excelMediumData.propertyNameTypeDic = new Dictionary<string, string>();
  237. //转换存储格式
  238. for (int i = 0; i < propertyCount; i++)
  239. {
  240. //数据名重复,数据无效
  241. if (excelMediumData.propertyNameTypeDic.ContainsKey(propertyNameTypes[i].Key))
  242. return null;
  243. excelMediumData.propertyNameTypeDic.Add(propertyNameTypes[i].Key, propertyNameTypes[i].Value);
  244. }
  245. excelMediumData.allItemValueRowList = allItemValueRowList;
  246. return excelMediumData;
  247. }
  248. //写文件
  249. private static bool WriteCodeStrToSave(string writeFilePath, string codeFileName, string classCodeStr)
  250. {
  251. if (string.IsNullOrEmpty(codeFileName) || string.IsNullOrEmpty(classCodeStr))
  252. return false;
  253. //检查导出路径
  254. if (!Directory.Exists(writeFilePath))
  255. Directory.CreateDirectory(writeFilePath);
  256. //写文件,生成CS类文件
  257. StreamWriter sw = new StreamWriter(writeFilePath + "/" + codeFileName + ".cs");
  258. sw.WriteLine(classCodeStr);
  259. sw.Close();
  260. //
  261. UnityEditor.AssetDatabase.Refresh();
  262. return true;
  263. }
  264. #endregion
  265. }
  266. #endif