• 【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件


    实现功能:

    • 自动创建继承ScriptableObject的C#数据类,每条Excel的数据,都有对应的字段的Get函数; 

     

    • 自动创建每个Excel的Asset生成类和生成函数,用于自动生成Asset文件

    • 使用Asset生成类自动序列化Excel数据到Asset文件,可直接在项目运行时加载使用

    实现原理:

    Excel配置格式:

    • 第1行对应特殊标记(可以设置有效性,指定要创建的文件)
    • 第2行对应中文说明(作为第3行字段的注释)
    • 第3行对应字段名称(自动创建的字段名称)
    • 第4行对应字段类型(自动创建的字段类型,与字段名称一一对应)
    • 第5行及以后对应字段值(所有数据,以行为单位解析、保存数据)
    • 第一列固定字段为"id",是代码中索引每行数据的Key

    Excel注释操作:

    • 字段名称行,每个字段单元格内容前加"//",可以注释该字段,不会解析生成到C#类;
    • 第一列的单元格内容前加"//",可以注释一行数据,不会保存到Asset文件中;
    • 注释可以用于添加说明行,或剔除指定无用数据。

    生成的C#类格式:

    行数据类,对应每一行数据:

    1. [Serializable]
    2. public class TestConfigExcelItem : ExcelItemBase
    3. {
    4. /// <summary>
    5. /// 数据id
    6. /// </summary>>
    7. public int id;
    8. /// <summary>
    9. /// 字符串
    10. /// </summary>>
    11. public string testString;
    12. /// <summary>
    13. /// Int
    14. /// </summary>>
    15. public int testInt;
    16. /// <summary>
    17. /// Float
    18. /// </summary>>
    19. public float testFloat;
    20. }

    完整数据类,包含所有行的数据、初始化函数、Get函数:

    1. public class TestConfigExcelData : ExcelDataBase<TestConfigExcelItem>
    2. {
    3. public TestConfigExcelItem[] items;
    4. public Dictionary<int,TestConfigExcelItem> itemDic = new Dictionary<int,TestConfigExcelItem>();
    5. public void Init()
    6. {
    7. itemDic.Clear();
    8. if(items != null && items.Length > 0)
    9. {
    10. for(int i = 0; i < items.Length; i++)
    11. {
    12. itemDic.Add(items[i].id, items[i]);
    13. }
    14. }
    15. }
    16. public TestConfigExcelItem GetTestConfigExcelItem(int id)
    17. {
    18. if(itemDic.ContainsKey(id))
    19. return itemDic[id];
    20. else
    21. return null;
    22. }
    23. #region --- Get Method ---
    24. public string GetTestString(int id)
    25. {
    26. var item = GetTestConfigExcelItem(id);
    27. if(item == null)
    28. return default;
    29. return item.testString;
    30. }
    31. // ··· ···
    32. #endregion
    33. }

    目前支持的数据结构:

    字符串testString字符串数组testStringArray字符串二维数组testStringArray2
    InttestIntInt数组testIntArrayInt二维数组testIntArray2
    FloattestFloatFloat数组testFloatArrayFloat二维数组testFloatArray2
    BooltestBoolBool数组testBoolArrayBool二维数组testBoolArray2
    Enum|枚举名(或枚举值)testEnumEnum数组testEnumArrayEnum二维数组不支持
    Vector2testVector2Vector2数组testVector2ArrayVector2二维数组testVector2Array2
    Vector3testVector3Vector3数组testVector3ArrayVector3二维数组testVector3Array2
    Vector2InttestVector2IntVector2Int数组testVector2IntArrayVector2Int二维数组testVector2IntArray2
    Vector3InttestVector3IntVector3Int数组testVector3IntArrayVector3Int二维数组testVector3IntArray2
    ColortestColorColor数组testColorArrayColor二维数组testColorArray2
    Color32testColor32Color32数组testColor32ArrayColor32二维数组testColor32Array2

    因为Unity不能序列化二维数组,这里改成一维数组+结构体的方式实现:

    1. [Serializable]
    2. public struct StringArr
    3. {
    4. public string[] array;
    5. }
    6. //二维数组表示方式: StringArr[]

    Asset数据文件:

    在自动生成数据的C#类时,会同步生成Asset文件的创建类,用于自动创建Asset文件并序列化数据。

    优点:

    • 数据修改后只需要重新一键生成即可
    • 每个Excel对应一个类,使用灵活,对Excel限制少
    • 自动创建C#类,不需要对每个Excel手动写代码,每条数据对应字段,不需要拆箱装修
    • 自动创建ScriptableObject的Asset文件,自动序列化数据,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作
    • 在游戏内直接读取Asset的ScriptableObject子类,不需要额外操作,业务层直接调取数据字段

    使用方法:

    • 按照标准格式配置Excel
    • 一键生成C#类、Asset文件
    • 项目运行时加载Asset资源,调用Init初始化,Get函数获取对应字段值即可

    完整代码:

    扩展Unity编辑器窗口:

    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.IO;
    4. using System.Collections.Generic;
    5. using System.Linq;
    6. public class BuildExcelWindow : EditorWindow
    7. {
    8. [MenuItem("MyTools/Excel Window",priority = 100)]
    9. public static void ShowReadExcelWindow()
    10. {
    11. BuildExcelWindow window = GetWindow<BuildExcelWindow>(true);
    12. window.Show();
    13. window.minSize = new Vector2(475,475);
    14. }
    15. //Excel读取路径,绝对路径,放在Assets同级路径
    16. private static string excelReadAbsolutePath;
    17. //自动生成C#类文件路径,绝对路径
    18. private static string scriptSaveAbsolutePath;
    19. private static string scriptSaveRelativePath;
    20. //自动生成Asset文件路径,相对路径
    21. private static string assetSaveRelativePath;
    22. private List<string> fileNameList = new List<string>();
    23. private List<string> filePathList = new List<string>();
    24. private void Awake()
    25. {
    26. titleContent.text = "Excel配置表读取";
    27. excelReadAbsolutePath = Application.dataPath.Replace("Assets","Excel");
    28. scriptSaveAbsolutePath = Application.dataPath + CheckEditorPath("/Script/Excel/AutoCreateCSCode");
    29. scriptSaveRelativePath = CheckEditorPath("Assets/Script/Excel/AutoCreateCSCode");
    30. assetSaveRelativePath = CheckEditorPath("Assets/AssetData/Excel/AutoCreateAsset");
    31. }
    32. private void OnEnable()
    33. {
    34. RefreshExcelFile();
    35. }
    36. private void OnDisable()
    37. {
    38. fileNameList.Clear();
    39. filePathList.Clear();
    40. }
    41. private Vector2 scrollPosition = Vector2.zero;
    42. private void OnGUI()
    43. {
    44. GUILayout.Space(10);
    45. scrollPosition = GUILayout.BeginScrollView(scrollPosition,GUILayout.Width(position.width),GUILayout.Height(position.height));
    46. //展示路径
    47. GUILayout.BeginHorizontal(GUILayout.Height(20));
    48. if(GUILayout.Button("Excel读取路径",GUILayout.Width(100)))
    49. {
    50. EditorUtility.OpenWithDefaultApp(excelReadAbsolutePath);
    51. Debug.Log(excelReadAbsolutePath);
    52. }
    53. if(GUILayout.Button("Script保存路径",GUILayout.Width(100)))
    54. {
    55. SelectObject(scriptSaveRelativePath);
    56. }
    57. if(GUILayout.Button("Asset保存路径",GUILayout.Width(100)))
    58. {
    59. SelectObject(assetSaveRelativePath);
    60. }
    61. GUILayout.EndHorizontal();
    62. GUILayout.Space(5);
    63. //Excel列表
    64. GUILayout.Label("Excel列表:");
    65. for(int i = 0; i < fileNameList.Count; i++)
    66. {
    67. GUILayout.BeginHorizontal("Box",GUILayout.Height(40));
    68. GUILayout.Label($"{i}:","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
    69. GUILayout.Box(fileNameList[i],"MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
    70. GUILayout.Space(10);
    71. //生成CS代码
    72. if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))
    73. {
    74. ExcelDataReader.ReadOneExcelToCode(filePathList[i],scriptSaveAbsolutePath);
    75. }
    76. //生成Asset文件
    77. if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))
    78. {
    79. ExcelDataReader.CreateOneExcelAsset(filePathList[i],assetSaveRelativePath);
    80. }
    81. GUILayout.EndHorizontal();
    82. GUILayout.Space(5);
    83. }
    84. GUILayout.Space(10);
    85. //一键处理所有Excel
    86. GUILayout.Label("一键操作:");
    87. GUILayout.BeginHorizontal("Box",GUILayout.Height(40));
    88. GUILayout.Label("all","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
    89. GUILayout.Box("All Excel","MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
    90. GUILayout.Space(10);
    91. if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))
    92. {
    93. ExcelDataReader.ReadAllExcelToCode(excelReadAbsolutePath,scriptSaveAbsolutePath);
    94. }
    95. if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))
    96. {
    97. ExcelDataReader.CreateAllExcelAsset(excelReadAbsolutePath,assetSaveRelativePath);
    98. }
    99. GUILayout.EndHorizontal();
    100. //
    101. GUILayout.Space(20);
    102. //
    103. GUILayout.EndScrollView();
    104. }
    105. //读取指定路径下的Excel文件名
    106. private void RefreshExcelFile()
    107. {
    108. fileNameList.Clear();
    109. filePathList.Clear();
    110. if(!Directory.Exists(excelReadAbsolutePath))
    111. {
    112. Debug.LogError("无效路径:" + excelReadAbsolutePath);
    113. return;
    114. }
    115. string[] excelFileFullPaths = Directory.GetFiles(excelReadAbsolutePath,"*.xlsx");
    116. if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
    117. {
    118. Debug.LogError(excelReadAbsolutePath + "路径下没有找到Excel文件");
    119. return;
    120. }
    121. filePathList.AddRange(excelFileFullPaths);
    122. for(int i = 0; i < filePathList.Count; i++)
    123. {
    124. fileNameList.Add(Path.GetFileName(filePathList[i]));
    125. }
    126. Debug.Log("找到Excel文件:" + fileNameList.Count + "个");
    127. }
    128. private void SelectObject(string targetPath)
    129. {
    130. Object targetObj = AssetDatabase.LoadAssetAtPath<Object>(targetPath);
    131. EditorGUIUtility.PingObject(targetObj);
    132. Selection.activeObject = targetObj;
    133. Debug.Log(targetPath);
    134. }
    135. private static string CheckEditorPath(string path)
    136. {
    137. #if UNITY_EDITOR_WIN
    138. return path.Replace("/","\\");
    139. #elif UNITY_EDITOR_OSX
    140. return path.Replace("\\","/");
    141. #else
    142. return path;
    143. #endif
    144. }
    145. }

    Excel数据读取类: 

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.IO;
    5. using Excel;
    6. using System.Reflection;
    7. using System;
    8. using System.Linq;
    9. public class ExcelDataReader
    10. {
    11. //Excel第1行对应特殊标记
    12. private const int specialSignRow = 0;
    13. //Excel第2行对应中文说明
    14. private const int excelNodeRow = 1;
    15. //Excel第3行对应字段名称
    16. private const int excelNameRow = 2;
    17. //Excel第4行对应字段类型
    18. private const int excelTypeRow = 3;
    19. //Excel第5行及以后对应字段值
    20. private const int excelDataRow = 4;
    21. //标记注释行/列
    22. private const string annotationSign = "//";
    23. #region --- Read Excel ---
    24. //创建Excel对应的C#类
    25. public static void ReadAllExcelToCode(string allExcelPath,string codeSavePath)
    26. {
    27. //读取所有Excel文件
    28. //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
    29. string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
    30. if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
    31. {
    32. Debug.Log("Excel file count == 0");
    33. return;
    34. }
    35. //遍历所有Excel,创建C#类
    36. for(int i = 0; i < excelFileFullPaths.Length; i++)
    37. {
    38. ReadOneExcelToCode(excelFileFullPaths[i],codeSavePath);
    39. }
    40. }
    41. //创建Excel对应的C#类
    42. public static void ReadOneExcelToCode(string excelFullPath,string codeSavePath)
    43. {
    44. //解析Excel获取中间数据
    45. ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
    46. if(excelMediumData == null)
    47. {
    48. Debug.LogError($"读取Excel失败 : {excelFullPath}");
    49. return;
    50. }
    51. if(!excelMediumData.isValid)
    52. {
    53. Debug.LogError($"读取Excel失败,Excel标记失效 : {excelMediumData.excelName}");
    54. return;
    55. }
    56. if(!excelMediumData.isCreateCSharp && !excelMediumData.isCreateAssignment)
    57. {
    58. Debug.LogError($"读取Excel失败,Excel不允许生成CSCode : {excelMediumData.excelName}");
    59. return;
    60. }
    61. //根据数据生成C#脚本
    62. string classCodeStr = ExcelCodeCreater.CreateCodeStrByExcelData(excelMediumData);
    63. if(string.IsNullOrEmpty(classCodeStr))
    64. {
    65. Debug.LogError($"解析Excel失败 : {excelMediumData.excelName}");
    66. return;
    67. }
    68. //检查导出路径
    69. if(!Directory.Exists(codeSavePath))
    70. Directory.CreateDirectory(codeSavePath);
    71. //类名
    72. string codeFileName = excelMediumData.excelName + "ExcelData";
    73. //写文件,生成CS类文件
    74. StreamWriter sw = new StreamWriter($"{codeSavePath}/{codeFileName}.cs");
    75. sw.WriteLine(classCodeStr);
    76. sw.Close();
    77. //
    78. UnityEditor.AssetDatabase.SaveAssets();
    79. UnityEditor.AssetDatabase.Refresh();
    80. //
    81. Debug.Log($"生成Excel的CS成功 : {excelMediumData.excelName}");
    82. }
    83. #endregion
    84. #region --- Create Asset ---
    85. //创建Excel对应的Asset数据文件
    86. public static void CreateAllExcelAsset(string allExcelPath,string assetSavePath)
    87. {
    88. //读取所有Excel文件
    89. //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
    90. string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
    91. if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)
    92. {
    93. Debug.Log("Excel file count == 0");
    94. return;
    95. }
    96. //遍历所有Excel,创建Asset
    97. for(int i = 0; i < excelFileFullPaths.Length; i++)
    98. {
    99. CreateOneExcelAsset(excelFileFullPaths[i],assetSavePath);
    100. }
    101. }
    102. //创建Excel对应的Asset数据文件
    103. public static void CreateOneExcelAsset(string excelFullPath,string assetSavePath)
    104. {
    105. //解析Excel获取中间数据
    106. ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
    107. if(excelMediumData == null)
    108. {
    109. Debug.LogError($"读取Excel失败 : {excelFullPath}");
    110. return;
    111. }
    112. if(!excelMediumData.isValid)
    113. {
    114. Debug.LogError($"读取Excel失败,Excel标记失效 : {excelMediumData.excelName}");
    115. return;
    116. }
    117. if(!excelMediumData.isCreateAsset)
    118. {
    119. Debug.LogError($"读取Excel失败,Excel不允许生成Asset : {excelMediumData.excelName}");
    120. return;
    121. }
    122. 获取当前程序集
    123. //Assembly assembly = Assembly.GetExecutingAssembly();
    124. 创建类的实例,返回为 object 类型,需要强制类型转换,assembly.CreateInstance("类的完全限定名(即包括命名空间)");
    125. //object class0bj = assembly.CreateInstance(excelMediumData.excelName + "Assignment",true);
    126. //必须遍历所有程序集来获得类型。当前在Assembly-CSharp-Editor中,目标类型在Assembly-CSharp中,不同程序将无法获取类型
    127. Type assignmentType = null;
    128. string assetAssignmentName = excelMediumData.excelName + "AssetAssignment";
    129. foreach(var asm in AppDomain.CurrentDomain.GetAssemblies())
    130. {
    131. //查找目标类型
    132. Type tempType = asm.GetType(assetAssignmentName);
    133. if(tempType != null)
    134. {
    135. assignmentType = tempType;
    136. break;
    137. }
    138. }
    139. if(assignmentType == null)
    140. {
    141. Debug.LogError($"创界Asset失败,未找到Asset生成类 : {excelMediumData.excelName}");
    142. return;
    143. }
    144. //反射获取方法
    145. MethodInfo methodInfo = assignmentType.GetMethod("CreateAsset");
    146. if(methodInfo == null)
    147. {
    148. if(assignmentType == null)
    149. {
    150. Debug.LogError($"创界Asset失败,未找到Asset创建函数 : {excelMediumData.excelName}");
    151. return;
    152. }
    153. }
    154. methodInfo.Invoke(null,new object[] { excelMediumData,assetSavePath });
    155. //创建Asset文件成功
    156. Debug.Log($"生成Excel的Asset成功 : {excelMediumData.excelName}");
    157. }
    158. #endregion
    159. #region --- private ---
    160. //解析Excel,创建中间数据
    161. private static ExcelMediumData CreateClassCodeByExcelPath(string excelFileFullPath)
    162. {
    163. if(string.IsNullOrEmpty(excelFileFullPath))
    164. return null;
    165. excelFileFullPath = excelFileFullPath.Replace("\\","/");
    166. //读取Excel
    167. FileStream stream = File.Open(excelFileFullPath,FileMode.Open,FileAccess.Read);
    168. if(stream == null)
    169. return null;
    170. //解析Excel
    171. IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
    172. //无效Excel
    173. if(excelReader == null || !excelReader.IsValid)
    174. {
    175. Debug.Log("Invalid excel : " + excelFileFullPath);
    176. return null;
    177. }
    178. Debug.Log("开始解析Excel : " + excelReader.Name);
    179. //记录Excel数据
    180. ExcelMediumData excelMediumData = new ExcelMediumData();
    181. //Excel名字
    182. excelMediumData.excelName = excelReader.Name;
    183. //当前遍历的行
    184. int curRowIndex = 0;
    185. //开始读取,按行遍历
    186. while(excelReader.Read())
    187. {
    188. //这一行没有读取到数据,视为无效行数据
    189. if(excelReader.FieldCount <= 0)
    190. {
    191. curRowIndex++;
    192. continue;
    193. }
    194. //读取每一行的完整数据
    195. string[] datas = new string[excelReader.FieldCount];
    196. for(int j = 0; j < excelReader.FieldCount; ++j)
    197. {
    198. //可以直接读取指定类型数据,不过只支持有限数据类型,这里统一读取string,然后再数据转化
    199. //excelReader.GetInt32(j); excelReader.GetFloat(j);
    200. //读取每一个单元格数据
    201. datas[j] = excelReader.GetString(j);
    202. }
    203. switch(curRowIndex)
    204. {
    205. case specialSignRow:
    206. //特殊标记行
    207. string specialSignStr = datas[0];
    208. if(specialSignStr.Length >= 4)
    209. {
    210. excelMediumData.isValid = specialSignStr[0] == 'T';
    211. excelMediumData.isCreateCSharp = specialSignStr[1] == 'T';
    212. excelMediumData.isCreateAssignment = specialSignStr[2] == 'T';
    213. excelMediumData.isCreateAsset = specialSignStr[3] == 'T';
    214. }
    215. else
    216. {
    217. Debug.LogError("未解析到特殊标记");
    218. }
    219. break;
    220. case excelNodeRow:
    221. //数据注释行
    222. excelMediumData.propertyNodeArray = datas;
    223. break;
    224. case excelNameRow:
    225. //数据名称行
    226. excelMediumData.propertyNameArray = datas;
    227. //注释列号
    228. for(int i = 0; i < datas.Length; i++)
    229. {
    230. if(string.IsNullOrEmpty(datas[i]) || datas[i].StartsWith(annotationSign))
    231. excelMediumData.annotationColList.Add(i);
    232. }
    233. break;
    234. case excelTypeRow:
    235. //数据类型行
    236. excelMediumData.propertyTypeArray = datas;
    237. break;
    238. default:
    239. //数据内容行
    240. excelMediumData.allRowItemList.Add(datas);
    241. //注释行号
    242. if(string.IsNullOrEmpty(datas[0]) || datas[0].StartsWith(annotationSign))
    243. excelMediumData.annotationRowList.Add(excelMediumData.allRowItemList.Count - 1);
    244. break;
    245. }
    246. //
    247. curRowIndex++;
    248. }
    249. if(CheckExcelMediumData(ref excelMediumData))
    250. {
    251. Debug.Log("读取Excel成功");
    252. return excelMediumData;
    253. }
    254. else
    255. {
    256. Debug.LogError("读取Excel失败");
    257. return null;
    258. }
    259. }
    260. //校验Excel数据
    261. private static bool CheckExcelMediumData(ref ExcelMediumData mediumData)
    262. {
    263. if(mediumData == null)
    264. return false;
    265. //检查数据有效性
    266. if(!mediumData.isValid)
    267. {
    268. Debug.LogError("Excel被标记无效");
    269. return false;
    270. }
    271. if(string.IsNullOrEmpty(mediumData.excelName))
    272. {
    273. Debug.LogError("Excel名字为空");
    274. return false;
    275. }
    276. if(mediumData.propertyNameArray == null || mediumData.propertyNameArray.Length == 0)
    277. {
    278. Debug.LogError("未解析到数据名称");
    279. return false;
    280. }
    281. if(mediumData.propertyTypeArray == null || mediumData.propertyTypeArray.Length == 0)
    282. {
    283. Debug.LogError("未解析到数据类型");
    284. return false;
    285. }
    286. if(mediumData.propertyNameArray.Length != mediumData.propertyTypeArray.Length)
    287. {
    288. Debug.LogError("数据名称与数据类型数量不一致");
    289. return false;
    290. }
    291. if(mediumData.allRowItemList.Count == 0)
    292. {
    293. Debug.LogError("数据内容为空");
    294. return false;
    295. }
    296. if(mediumData.propertyNameArray[0] != "id")
    297. {
    298. Debug.LogError("第一个字段必须是id字段");
    299. return false;
    300. }
    301. return true;
    302. }
    303. #endregion
    304. }

    C#代码生成类:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Text;
    5. using System.Linq;
    6. using System;
    7. public class ExcelCodeCreater
    8. {
    9. //创建代码,生成数据C#类
    10. public static string CreateCodeStrByExcelData(ExcelMediumData excelMediumData)
    11. {
    12. if(excelMediumData == null)
    13. return null;
    14. //行数据类名
    15. string itemClassName = excelMediumData.excelName + "ExcelItem";
    16. //整体数据类名
    17. string dataClassName = excelMediumData.excelName + "ExcelData";
    18. //开始生成类
    19. StringBuilder classSource = new StringBuilder();
    20. classSource.AppendLine("/*Auto Create, Don't Edit !!!*/");
    21. classSource.AppendLine();
    22. //添加引用
    23. classSource.AppendLine("using UnityEngine;");
    24. classSource.AppendLine("using System.Collections.Generic;");
    25. classSource.AppendLine("using System;");
    26. classSource.AppendLine("using System.IO;");
    27. classSource.AppendLine();
    28. //生成CSharp数据类
    29. if(excelMediumData.isCreateCSharp)
    30. {
    31. //生成行数据类,记录每行数据
    32. classSource.AppendLine(CreateExcelRowItemClass(itemClassName,excelMediumData));
    33. classSource.AppendLine();
    34. //生成整体数据类,记录整个Excel的所有行数据
    35. classSource.AppendLine(CreateExcelAllDataClass(dataClassName,itemClassName,excelMediumData));
    36. classSource.AppendLine();
    37. }
    38. //生成Asset创建类
    39. if(excelMediumData.isCreateAssignment)
    40. {
    41. //生成Asset操作类,用于自动创建Excel对应的Asset文件并赋值
    42. classSource.AppendLine(CreateExcelAssetClass(excelMediumData));
    43. classSource.AppendLine();
    44. }
    45. //
    46. return classSource.ToString();
    47. }
    48. //----------
    49. //生成行数据类
    50. private static string CreateExcelRowItemClass(string itemClassName,ExcelMediumData excelMediumData)
    51. {
    52. //生成Excel行数据类
    53. StringBuilder classSource = new StringBuilder();
    54. //类名
    55. classSource.AppendLine("[Serializable]");
    56. classSource.AppendLine($"public class {itemClassName} : ExcelItemBase");
    57. classSource.AppendLine("{");
    58. //声明所有字段
    59. for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)
    60. {
    61. //跳过注释字段
    62. if(excelMediumData.annotationColList.Contains(i))
    63. continue;
    64. //添加注释
    65. if(i < excelMediumData.propertyNodeArray.Length)
    66. {
    67. string propertyNode = excelMediumData.propertyNodeArray[i];
    68. if(!string.IsNullOrEmpty(propertyNode))
    69. {
    70. classSource.AppendLine("\t/// <summary>");
    71. classSource.AppendLine($"\t/// {propertyNode}");
    72. classSource.AppendLine("\t/// </summary>>");
    73. }
    74. }
    75. //声明行数据类的字段
    76. string propertyName = excelMediumData.propertyNameArray[i];
    77. string propertyType = excelMediumData.propertyTypeArray[i];
    78. string typeStr = GetPropertyType(propertyType);
    79. classSource.AppendLine($"\tpublic {typeStr} {propertyName};");
    80. }
    81. classSource.AppendLine("}");
    82. return classSource.ToString();
    83. }
    84. //----------
    85. //生成整体数据类
    86. private static string CreateExcelAllDataClass(string dataClassName,string itemClassName,ExcelMediumData excelMediumData)
    87. {
    88. StringBuilder classSource = new StringBuilder();
    89. //类名
    90. classSource.AppendLine($"public class {dataClassName} : ExcelDataBase<{itemClassName}>");
    91. classSource.AppendLine("{");
    92. //声明字段,行数据类数组
    93. classSource.AppendLine($"\tpublic {itemClassName}[] items;");
    94. classSource.AppendLine();
    95. //id字段类型
    96. string idTypeStr = GetPropertyType(excelMediumData.propertyTypeArray[0]);
    97. //声明字典
    98. classSource.AppendLine($"\tpublic Dictionary<{idTypeStr},{itemClassName}> itemDic = new Dictionary<{idTypeStr},{itemClassName}>();");
    99. classSource.AppendLine();
    100. //字段初始化方法
    101. classSource.AppendLine("\tpublic void Init()");
    102. classSource.AppendLine("\t{");
    103. classSource.AppendLine("\t\titemDic.Clear();");
    104. classSource.AppendLine("\t\tif(items != null && items.Length > 0)");
    105. classSource.AppendLine("\t\t{");
    106. classSource.AppendLine("\t\t\tfor(int i = 0; i < items.Length; i++)");
    107. classSource.AppendLine("\t\t\t{");
    108. classSource.AppendLine("\t\t\t\titemDic.Add(items[i].id, items[i]);");
    109. classSource.AppendLine("\t\t\t}");
    110. classSource.AppendLine("\t\t}");
    111. classSource.AppendLine("\t}");
    112. classSource.AppendLine();
    113. //字典获取方法
    114. classSource.AppendLine($"\tpublic {itemClassName} Get{itemClassName}({idTypeStr} id)");
    115. classSource.AppendLine("\t{");
    116. classSource.AppendLine("\t\tif(itemDic.ContainsKey(id))");
    117. classSource.AppendLine("\t\t\treturn itemDic[id];");
    118. classSource.AppendLine("\t\telse");
    119. classSource.AppendLine("\t\t\treturn null;");
    120. classSource.AppendLine("\t}");
    121. //每个字段Get函数
    122. classSource.AppendLine("\t#region --- Get Method ---");
    123. classSource.AppendLine();
    124. for(int i = 1; i < excelMediumData.propertyNameArray.Length; i++)
    125. {
    126. if(excelMediumData.annotationColList.Contains(i))
    127. continue;
    128. string propertyName = excelMediumData.propertyNameArray[i];
    129. string propertyType = excelMediumData.propertyTypeArray[i];
    130. //每个字段Get函数
    131. classSource.AppendLine(CreateCodePropertyMethod(itemClassName,idTypeStr,propertyName,propertyType));
    132. }
    133. classSource.AppendLine("\t#endregion");
    134. classSource.AppendLine("}");
    135. return classSource.ToString();
    136. }
    137. //生成数据字段对应Get方法
    138. private static string CreateCodePropertyMethod(string itemClassName,string idTypeStr,string propertyName,string propertyType)
    139. {
    140. StringBuilder methodBuilder = new StringBuilder();
    141. string itemNameStr = propertyName.FirstOrDefault().ToString().ToUpper() + propertyName.Substring(1);
    142. string itemTypeStr = GetPropertyType(propertyType);
    143. //字段Get函数
    144. methodBuilder.AppendLine($"\tpublic {itemTypeStr} Get{itemNameStr}({idTypeStr} id)");
    145. methodBuilder.AppendLine("\t{");
    146. methodBuilder.AppendLine($"\t\tvar item = Get{itemClassName}(id);");
    147. methodBuilder.AppendLine("\t\tif(item == null)");
    148. methodBuilder.AppendLine("\t\t\treturn default;");
    149. methodBuilder.AppendLine($"\t\treturn item.{propertyName};");
    150. methodBuilder.AppendLine("\t}");
    151. //如果是一维数组
    152. if(propertyType.Contains("[]"))
    153. {
    154. //typeStr:int[]或IntArr[] ,返回值:int或IntArr
    155. //string itemTypeStr1d = GetPropertyType(propertyType.Replace("[]",""));
    156. string itemTypeStr1d = itemTypeStr.Replace("[]","");
    157. methodBuilder.AppendLine($"\tpublic {itemTypeStr1d} Get{itemNameStr}({idTypeStr} id, int index)");
    158. methodBuilder.AppendLine("\t{");
    159. methodBuilder.AppendLine($"\t\tvar item0 = Get{itemClassName} (id);");
    160. methodBuilder.AppendLine("\t\tif(item0 == null)");
    161. methodBuilder.AppendLine("\t\t\treturn default;");
    162. methodBuilder.AppendLine($"\t\tvar item1 = item0.{propertyName};");
    163. methodBuilder.AppendLine("\t\tif(item1 == null || index < 0 || index >= item1.Length)");
    164. methodBuilder.AppendLine("\t\t\treturn default;");
    165. methodBuilder.AppendLine("\t\treturn item1[index];");
    166. methodBuilder.AppendLine("\t}");
    167. }
    168. //如果是二维数组
    169. if(propertyType.Contains("[][]"))
    170. {
    171. //propertyType:int[][], 返回值:int
    172. string itemTypeStr1d = GetPropertyType(propertyType.Replace("[][]",""));
    173. methodBuilder.AppendLine($"\tpublic {itemTypeStr1d} Get{itemNameStr}({idTypeStr} id, int index1, int index2)");
    174. methodBuilder.AppendLine("\t{");
    175. methodBuilder.AppendLine($"\t\tvar item0 = Get{itemClassName}(id);");
    176. methodBuilder.AppendLine("\t\tif(item0 == null)");
    177. methodBuilder.AppendLine("\t\t\treturn default;");
    178. methodBuilder.AppendLine($"\t\tvar item1 = item0.{propertyName};");
    179. methodBuilder.AppendLine("\t\tif(item1 == null || index1 < 0 || index1 >= item1.Length)");
    180. methodBuilder.AppendLine("\t\t\treturn default;");
    181. methodBuilder.AppendLine("\t\tvar item2 = item1[index1];");
    182. methodBuilder.AppendLine("\t\tif(item2.array == null || index2 < 0 || index2 >= item2.array.Length)");
    183. methodBuilder.AppendLine("\t\t\treturn default;");
    184. methodBuilder.AppendLine("\t\treturn item2.array[index2];");
    185. methodBuilder.AppendLine("\t}");
    186. }
    187. //
    188. return methodBuilder.ToString();
    189. }
    190. //----------
    191. //生成Asset创建类
    192. private static string CreateExcelAssetClass(ExcelMediumData excelMediumData)
    193. {
    194. string itemClassName = excelMediumData.excelName + "ExcelItem";
    195. string dataClassName = excelMediumData.excelName + "ExcelData";
    196. string assignmentClassName = excelMediumData.excelName + "AssetAssignment";
    197. StringBuilder classSource = new StringBuilder();
    198. classSource.AppendLine("#if UNITY_EDITOR");
    199. //类名
    200. classSource.AppendLine($"public class {assignmentClassName}");
    201. classSource.AppendLine("{");
    202. //方法名
    203. classSource.AppendLine("\tpublic static bool CreateAsset(ExcelMediumData excelMediumData, string excelAssetPath)");
    204. //方法体,若有需要可加入try/catch
    205. classSource.AppendLine("\t{");
    206. classSource.AppendLine("\t\tvar allRowItemDicList = excelMediumData.GetAllRowItemDicList();");
    207. classSource.AppendLine("\t\tif(allRowItemDicList == null || allRowItemDicList.Count == 0)");
    208. classSource.AppendLine("\t\t\treturn false;");
    209. classSource.AppendLine();
    210. classSource.AppendLine("\t\tint rowCount = allRowItemDicList.Count;");
    211. classSource.AppendLine($"\t\t{dataClassName} excelDataAsset = ScriptableObject.CreateInstance<{dataClassName}>();");
    212. classSource.AppendLine($"\t\texcelDataAsset.items = new {itemClassName}[rowCount];");
    213. classSource.AppendLine();
    214. classSource.AppendLine("\t\tfor(int i = 0; i < rowCount; i++)");
    215. classSource.AppendLine("\t\t{");
    216. classSource.AppendLine("\t\t\tvar itemRowDic = allRowItemDicList[i];");
    217. classSource.AppendLine($"\t\t\texcelDataAsset.items[i] = new {itemClassName}();");
    218. for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)
    219. {
    220. if(excelMediumData.annotationColList.Contains(i))
    221. continue;
    222. string propertyName = excelMediumData.propertyNameArray[i];
    223. string propertyType = excelMediumData.propertyTypeArray[i];
    224. classSource.Append($"\t\t\texcelDataAsset.items[i].{propertyName} = ");
    225. classSource.Append(AssignmentCodeProperty(propertyName,propertyType));
    226. classSource.AppendLine(";");
    227. }
    228. classSource.AppendLine("\t\t}");
    229. classSource.AppendLine("\t\tif(!Directory.Exists(excelAssetPath))");
    230. classSource.AppendLine("\t\t\tDirectory.CreateDirectory(excelAssetPath);");
    231. classSource.AppendLine($"\t\tstring fullPath = Path.Combine(excelAssetPath,typeof({dataClassName}).Name) + \".asset\";");
    232. classSource.AppendLine("\t\tUnityEditor.AssetDatabase.DeleteAsset(fullPath);");
    233. classSource.AppendLine("\t\tUnityEditor.AssetDatabase.CreateAsset(excelDataAsset,fullPath);");
    234. classSource.AppendLine("\t\tUnityEditor.AssetDatabase.Refresh();");
    235. classSource.AppendLine("\t\treturn true;");
    236. classSource.AppendLine("\t}");
    237. //
    238. classSource.AppendLine("}");
    239. classSource.AppendLine("#endif");
    240. return classSource.ToString();
    241. }
    242. //声明Asset操作类字段
    243. private static string AssignmentCodeProperty(string propertyName,string propertyType)
    244. {
    245. string stringValue = $"itemRowDic[\"{propertyName}\"]";
    246. string typeStr = GetPropertyType(propertyType);
    247. switch(typeStr)
    248. {
    249. //字段
    250. case "int":
    251. return "StringUtility.StringToInt(" + stringValue + ")";
    252. case "float":
    253. return "StringUtility.StringToFloat(" + stringValue + ")";
    254. case "bool":
    255. return "StringUtility.StringToBool(" + stringValue + ")";
    256. case "Vector2":
    257. return "StringUtility.StringToVector2(" + stringValue + ")";
    258. case "Vector3":
    259. return "StringUtility.StringToVector3(" + stringValue + ")";
    260. case "Vector2Int":
    261. return "StringUtility.StringToVector2Int(" + stringValue + ")";
    262. case "Vector3Int":
    263. return "StringUtility.StringToVector3Int(" + stringValue + ")";
    264. case "Color":
    265. return "StringUtility.StringToColor(" + stringValue + ")";
    266. case "Color32":
    267. return "StringUtility.StringToColor32(" + stringValue + ")";
    268. case "string":
    269. return stringValue;
    270. //一维
    271. case "int[]":
    272. return "StringUtility.StringToIntArray(" + stringValue + ")";
    273. case "float[]":
    274. return "StringUtility.StringToFloatArray(" + stringValue + ")";
    275. case "bool[]":
    276. return "StringUtility.StringToBoolArray(" + stringValue + ")";
    277. case "Vector2[]":
    278. return "StringUtility.StringToVector2Array(" + stringValue + ")";
    279. case "Vector3[]":
    280. return "StringUtility.StringToVector3Array(" + stringValue + ")";
    281. case "Vector2Int[]":
    282. return "StringUtility.StringToVector2IntArray(" + stringValue + ")";
    283. case "Vector3Int[]":
    284. return "StringUtility.StringToVector3IntArray(" + stringValue + ")";
    285. case "Color[]":
    286. return "StringUtility.StringToColorArray(" + stringValue + ")";
    287. case "Color32[]":
    288. return "StringUtility.StringToColor32Array(" + stringValue + ")";
    289. case "string[]":
    290. return "StringUtility.StringToStringArray(" + stringValue + ")";
    291. //二维
    292. case "IntArr[]":
    293. return "StringUtility.StringToIntArray2D(" + stringValue + ")";
    294. case "FloatArr[]":
    295. return "StringUtility.StringToFloatArray2D(" + stringValue + ")";
    296. case "BoolArr[]":
    297. return "StringUtility.StringToBoolArray2D(" + stringValue + ")";
    298. case "Vector2Arr[]":
    299. return "StringUtility.StringToVector2Array2D(" + stringValue + ")";
    300. case "Vector3Arr[]":
    301. return "StringUtility.StringToVector3Array2D(" + stringValue + ")";
    302. case "Vector2IntArr[]":
    303. return "StringUtility.StringToVector2IntArray2D(" + stringValue + ")";
    304. case "Vector3IntArr[]":
    305. return "StringUtility.StringToVector3IntArray2D(" + stringValue + ")";
    306. case "ColorArr[]":
    307. return "StringUtility.StringToColorArray2D(" + stringValue + ")";
    308. case "Color32Arr[]":
    309. return "StringUtility.StringToColor32Array2D(" + stringValue + ")";
    310. case "StringArr[]":
    311. return "StringUtility.StringToStringArray2D(" + stringValue + ")";
    312. default:
    313. //枚举
    314. if(propertyType.StartsWith("enum"))
    315. {
    316. string enumType = propertyType.Split('|').FirstOrDefault();
    317. string enumName = propertyType.Split('|').LastOrDefault();
    318. if(enumType == "enum")
    319. return "StringUtility.StringToEnum<" + enumName + ">(" + stringValue + ")";
    320. else if(enumType == "enum[]")
    321. return "StringUtility.StringToEnumArray<" + enumName + ">(" + stringValue + ")";
    322. else if(enumType == "enum[][]")
    323. return "StringUtility.StringToEnumArray2D<" + enumName + ">(" + stringValue + ")";
    324. }
    325. return stringValue;
    326. }
    327. }
    328. //判断字段类型
    329. private static string GetPropertyType(string propertyType)
    330. {
    331. string lowerType = propertyType.ToLower();
    332. switch(lowerType)
    333. {
    334. case "int":
    335. return "int";
    336. case "int[]":
    337. return "int[]";
    338. case "int[][]":
    339. return "IntArr[]";
    340. case "float":
    341. return "float";
    342. case "float[]":
    343. return "float[]";
    344. case "float[][]":
    345. return "FloatArr[]";
    346. case "bool":
    347. return "bool";
    348. case "bool[]":
    349. return "bool[]";
    350. case "bool[][]":
    351. return "BoolArr[]";
    352. case "string":
    353. return "string";
    354. case "string[]":
    355. return "string[]";
    356. case "string[][]":
    357. return "StringArr[]";
    358. case "vector2":
    359. return "Vector2";
    360. case "vector2[]":
    361. return "Vector2[]";
    362. case "vector2[][]":
    363. return "Vector2Arr[]";
    364. case "vector2int":
    365. return "Vector2Int";
    366. case "vector2int[]":
    367. return "Vector2Int[]";
    368. case "vector2int[][]":
    369. return "Vector2IntArr[]";
    370. case "vector3":
    371. return "Vector3";
    372. case "vector3[]":
    373. return "Vector3[]";
    374. case "vector3[][]":
    375. return "Vector3Arr[]";
    376. case "vector3int":
    377. return "Vector3Int";
    378. case "vector3int[]":
    379. return "Vector3Int[]";
    380. case "vector3int[][]":
    381. return "Vector3IntArr[]";
    382. case "color":
    383. return "Color";
    384. case "color[]":
    385. return "Color[]";
    386. case "color[][]":
    387. return "ColorArr[]";
    388. case "color32":
    389. return "Color32";
    390. case "color32[]":
    391. return "Color32[]";
    392. case "color32[][]":
    393. return "Color32Arr[]";
    394. default:
    395. if(propertyType.StartsWith("enum"))
    396. {
    397. string enumType = propertyType.Split('|').FirstOrDefault();
    398. string enumName = propertyType.Split('|').LastOrDefault();
    399. switch(enumType)
    400. {
    401. case "enum":
    402. return enumName;
    403. case "enum[]":
    404. return $"{enumName}[]";
    405. case "enum[][]":
    406. return $"EnumArr<{enumName}>[]";
    407. default:
    408. break;
    409. }
    410. }
    411. return "string";
    412. }
    413. }
    414. }

     Excel数据中间类:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. //Excel中间数据
    5. public class ExcelMediumData
    6. {
    7. //Excel名字
    8. public string excelName;
    9. //Excel是否有效
    10. public bool isValid = false;
    11. //是否生成CSharp数据类
    12. public bool isCreateCSharp = false;
    13. //是否生成Asset创建类
    14. public bool isCreateAssignment = false;
    15. //是否生成Asset文件
    16. public bool isCreateAsset = false;
    17. //数据注释
    18. public string[] propertyNodeArray = null;
    19. //数据名称
    20. public string[] propertyNameArray = null;
    21. //数据类型
    22. public string[] propertyTypeArray = null;
    23. //List<每行数据内容>
    24. public List<string[]> allRowItemList = new List<string[]>();
    25. //注释行号
    26. public List<int> annotationRowList = new List<int>();
    27. //注释列号
    28. public List<int> annotationColList = new List<int>();
    29. //List<每行数据>,List<Dictionary<单元格字段名称, 单元格字段值>>
    30. public List<Dictionary<string,string>> GetAllRowItemDicList()
    31. {
    32. if(propertyNameArray == null || propertyNameArray.Length == 0)
    33. return null;
    34. if(allRowItemList.Count == 0)
    35. return null;
    36. List<Dictionary<string,string>> allRowItemDicList = new List<Dictionary<string,string>>(allRowItemList.Count);
    37. for(int i = 0; i < allRowItemList.Count; i++)
    38. {
    39. string[] rowArray = allRowItemList[i];
    40. //跳过空数据
    41. if(rowArray == null || rowArray.Length == 0)
    42. continue;
    43. //跳过注释数据
    44. if(annotationRowList.Contains(i))
    45. continue;
    46. //每行数据,对应字段名称和字段值
    47. Dictionary<string,string> rowDic = new Dictionary<string,string>();
    48. for(int j = 0; j < propertyNameArray.Length; j++)
    49. {
    50. //跳过注释字段
    51. if(annotationColList.Contains(j))
    52. continue;
    53. string propertyName = propertyNameArray[j];
    54. string propertyValue = j < rowArray.Length ? rowArray[j] : null;
    55. rowDic[propertyName] = propertyValue;
    56. }
    57. allRowItemDicList.Add(rowDic);
    58. }
    59. return allRowItemDicList;
    60. }
    61. }

    Excel数据基类、扩展类:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Linq;
    5. using System;
    6. public class ExcelDataBase<T> : ScriptableObject where T : ExcelItemBase
    7. {
    8. }
    9. public class ExcelItemBase
    10. {
    11. }
    12. [Serializable]
    13. public struct StringArr
    14. {
    15. public string[] array;
    16. }
    17. [Serializable]
    18. public struct IntArr
    19. {
    20. public int[] array;
    21. }
    22. [Serializable]
    23. public struct FloatArr
    24. {
    25. public float[] array;
    26. }
    27. [Serializable]
    28. public struct BoolArr
    29. {
    30. public bool[] array;
    31. }
    32. [Serializable]
    33. public struct Vector2Arr
    34. {
    35. public Vector2[] array;
    36. }
    37. [Serializable]
    38. public struct Vector3Arr
    39. {
    40. public Vector3[] array;
    41. }
    42. [Serializable]
    43. public struct Vector2IntArr
    44. {
    45. public Vector2Int[] array;
    46. }
    47. [Serializable]
    48. public struct Vector3IntArr
    49. {
    50. public Vector3Int[] array;
    51. }
    52. [Serializable]
    53. public struct ColorArr
    54. {
    55. public Color[] array;
    56. }
    57. [Serializable]
    58. public struct Color32Arr
    59. {
    60. public Color32[] array;
    61. }
    62. 不支持泛型枚举序列化
    63. //[Serializable]
    64. //public struct EnumArr<T> where T : Enum
    65. //{
    66. // public T[] array;
    67. //}

    字符串工具类:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Text.RegularExpressions;
    5. using System;
    6. using System.Text;
    7. using System.Linq;
    8. using System.Runtime.CompilerServices;
    9. public static class StringUtility
    10. {
    11. #region --- AddColor ---
    12. public static string AddColor(object obj,Color color)
    13. {
    14. return AddColor(obj,color);
    15. }
    16. public static string AddColor(this string str,Color color)
    17. {
    18. //把颜色转换为16进制字符串,添加到富文本
    19. return string.Format("<color=#{0}>{1}</color>",ColorUtility.ToHtmlStringRGBA(color),str);
    20. }
    21. public static string AddColor(string str1,string str2,Color color)
    22. {
    23. return AddColor(str1 + str2,color);
    24. }
    25. public static string AddColor(string str1,string str2,string str3,Color color)
    26. {
    27. return AddColor(str1 + str2 + str3,color);
    28. }
    29. #endregion
    30. #region --- string length ---
    31. /// <summary>
    32. /// 化简字符串长度
    33. /// </summary>
    34. /// <param name="targetStr"></param>
    35. /// <param name="targetLength">目标长度,英文字符==1,中文字符==2</param>
    36. /// <returns></returns>
    37. public static string AbbrevStringWithinLength(string targetStr,int targetLength,string abbrevPostfix)
    38. {
    39. //C#实际统计:一个中文字符长度==1,英文字符长度==1
    40. //UI显示要求:一个中文字符长度==2,英文字符长度==1
    41. //校验参数
    42. if(string.IsNullOrEmpty(targetStr) || targetLength <= 0)
    43. return targetStr;
    44. //字符串长度 * 2 <= 目标长度,即使是全中文也在长度范围内
    45. if(targetStr.Length * 2 <= targetLength)
    46. return targetStr;
    47. //遍历字符
    48. char[] chars = targetStr.ToCharArray();
    49. int curLen = 0;
    50. for(int i = 0; i < chars.Length; i++)
    51. {
    52. //累加字符串长度
    53. if(chars[i] >= 0x4e00 && chars[i] <= 0x9fbb)
    54. curLen += 2;
    55. else
    56. curLen += 1;
    57. //如果当前位置累计长度超过目标长度,取0~i-1,即Substring(0,i)
    58. if(curLen > targetLength)
    59. return targetStr.Substring(0,i) + abbrevPostfix;
    60. }
    61. return targetStr;
    62. }
    63. #endregion
    64. #region --- String To Array ---
    65. //string
    66. public static byte StringToByte(string valueStr)
    67. {
    68. byte value;
    69. if(byte.TryParse(valueStr,out value))
    70. return value;
    71. else
    72. return 0;
    73. }
    74. public static string[] StringToStringArray(string valueStr,char splitSign = '|')
    75. {
    76. if(string.IsNullOrEmpty(valueStr))
    77. return null;
    78. return valueStr.Split(splitSign);
    79. }
    80. public static StringArr[] StringToStringArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    81. {
    82. if(string.IsNullOrEmpty(valueStr))
    83. return null;
    84. string[] strArr1 = valueStr.Split(splitSign1);
    85. if(strArr1.Length == 0)
    86. return null;
    87. StringArr[] arrArr = new StringArr[strArr1.Length];
    88. for(int i = 0; i < strArr1.Length; i++)
    89. {
    90. arrArr[i] = new StringArr()
    91. {
    92. array = strArr1[i].Split(splitSign2)
    93. };
    94. }
    95. return arrArr;
    96. }
    97. //int
    98. public static int StringToInt(string valueStr)
    99. {
    100. int value;
    101. if(int.TryParse(valueStr,out value))
    102. return value;
    103. else
    104. return 0;
    105. }
    106. public static int[] StringToIntArray(string valueStr,char splitSign = '|')
    107. {
    108. if(string.IsNullOrEmpty(valueStr))
    109. return null;
    110. string[] valueArr = valueStr.Split(splitSign);
    111. if(valueArr == null || valueArr.Length == 0)
    112. return null;
    113. int[] intArr = new int[valueArr.Length];
    114. for(int i = 0; i < valueArr.Length; i++)
    115. {
    116. intArr[i] = StringToInt(valueArr[i]);
    117. }
    118. return intArr;
    119. }
    120. public static IntArr[] StringToIntArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    121. {
    122. if(string.IsNullOrEmpty(valueStr))
    123. return null;
    124. string[] strArr1 = valueStr.Split(splitSign1);
    125. if(strArr1.Length == 0)
    126. return null;
    127. IntArr[] arrArr = new IntArr[strArr1.Length];
    128. for(int i = 0; i < strArr1.Length; i++)
    129. {
    130. arrArr[i] = new IntArr()
    131. {
    132. array = StringToIntArray(strArr1[i],splitSign2)
    133. };
    134. }
    135. return arrArr;
    136. }
    137. //float
    138. public static float StringToFloat(string valueStr)
    139. {
    140. float value;
    141. if(float.TryParse(valueStr,out value))
    142. return value;
    143. else
    144. return 0;
    145. }
    146. public static float[] StringToFloatArray(string valueStr,char splitSign = '|')
    147. {
    148. if(string.IsNullOrEmpty(valueStr))
    149. return null;
    150. string[] valueArr = valueStr.Split(splitSign);
    151. if(valueArr == null || valueArr.Length == 0)
    152. return null;
    153. float[] floatArr = new float[valueArr.Length];
    154. for(int i = 0; i < valueArr.Length; i++)
    155. {
    156. floatArr[i] = StringToFloat(valueArr[i]);
    157. }
    158. return floatArr;
    159. }
    160. public static FloatArr[] StringToFloatArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    161. {
    162. if(string.IsNullOrEmpty(valueStr))
    163. return null;
    164. string[] strArr1 = valueStr.Split(splitSign1);
    165. if(strArr1.Length == 0)
    166. return null;
    167. FloatArr[] arrArr = new FloatArr[strArr1.Length];
    168. for(int i = 0; i < strArr1.Length; i++)
    169. {
    170. arrArr[i] = new FloatArr()
    171. {
    172. array = StringToFloatArray(strArr1[i],splitSign2)
    173. };
    174. }
    175. return arrArr;
    176. }
    177. //bool
    178. public static bool StringToBool(string valueStr)
    179. {
    180. bool value;
    181. if(bool.TryParse(valueStr,out value))
    182. return value;
    183. else
    184. return false;
    185. }
    186. public static bool[] StringToBoolArray(string valueStr,char splitSign = '|')
    187. {
    188. if(string.IsNullOrEmpty(valueStr))
    189. return null;
    190. string[] valueArr = valueStr.Split(splitSign);
    191. if(valueArr == null || valueArr.Length == 0)
    192. return null;
    193. bool[] boolArr = new bool[valueArr.Length];
    194. for(int i = 0; i < valueArr.Length; i++)
    195. {
    196. boolArr[i] = StringToBool(valueArr[i]);
    197. }
    198. return boolArr;
    199. }
    200. public static BoolArr[] StringToBoolArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    201. {
    202. if(string.IsNullOrEmpty(valueStr))
    203. return null;
    204. string[] strArr1 = valueStr.Split(splitSign1);
    205. if(strArr1.Length == 0)
    206. return null;
    207. BoolArr[] arrArr = new BoolArr[strArr1.Length];
    208. for(int i = 0; i < strArr1.Length; i++)
    209. {
    210. arrArr[i] = new BoolArr()
    211. {
    212. array = StringToBoolArray(strArr1[i],splitSign2)
    213. };
    214. }
    215. return arrArr;
    216. }
    217. //enum
    218. public static T StringToEnum<T>(string valueStr) where T : Enum
    219. {
    220. if(string.IsNullOrEmpty(valueStr))
    221. return (T)default;
    222. //先校验字符串是否为枚举值
    223. int intValue;
    224. if(int.TryParse(valueStr,out intValue))
    225. {
    226. if(Enum.IsDefined(typeof(T),intValue))
    227. return (T)Enum.ToObject(typeof(T),intValue);
    228. }
    229. //如果不是枚举值,当做枚举名处理
    230. try
    231. {
    232. T t = (T)Enum.Parse(typeof(T),valueStr);
    233. if(Enum.IsDefined(typeof(T),t))
    234. return t;
    235. }
    236. catch(Exception e)
    237. {
    238. Debug.LogError(e);
    239. }
    240. Debug.LogError(string.Format("解析枚举错误 {0} : {1}",typeof(T),valueStr));
    241. return (T)default;
    242. }
    243. public static T[] StringToEnumArray<T>(string valueStr,char splitSign = '|') where T : Enum
    244. {
    245. if(string.IsNullOrEmpty(valueStr))
    246. return null;
    247. string[] valueArr = valueStr.Split(splitSign);
    248. if(valueArr == null || valueArr.Length == 0)
    249. return null;
    250. T[] enumArr = new T[valueArr.Length];
    251. for(int i = 0; i < valueArr.Length; i++)
    252. {
    253. enumArr[i] = StringToEnum<T>(valueArr[i]);
    254. }
    255. return enumArr;
    256. }
    257. 不支持泛型枚举序列化
    258. //public static EnumArr<T>[] StringToEnumArray2D<T>(string valueStr,char splitSign1 = '&',char splitSign2 = '|') where T : Enum
    259. //{
    260. // if(string.IsNullOrEmpty(valueStr))
    261. // return null;
    262. // string[] strArr1 = valueStr.Split(splitSign1);
    263. // if(strArr1.Length == 0)
    264. // return null;
    265. // EnumArr<T>[] arrArr = new EnumArr<T>[strArr1.Length];
    266. // for(int i = 0; i < strArr1.Length; i++)
    267. // {
    268. // arrArr[i] = new EnumArr<T>()
    269. // {
    270. // array = StringToEnumArray<T>(strArr1[i],splitSign2)
    271. // };
    272. // }
    273. // return arrArr;
    274. //}
    275. //vector2
    276. public static Vector2 StringToVector2(string valueStr,char splitSign = ',')
    277. {
    278. Vector2 value = Vector2.zero;
    279. if(!string.IsNullOrEmpty(valueStr))
    280. {
    281. string[] stringArray = valueStr.Split(splitSign);
    282. if(stringArray != null && stringArray.Length >= 2)
    283. {
    284. value.x = StringToFloat(stringArray[0]);
    285. value.y = StringToFloat(stringArray[1]);
    286. return value;
    287. }
    288. }
    289. Debug.LogWarning("String to Vector2 error");
    290. return value;
    291. }
    292. public static Vector2[] StringToVector2Array(string valueStr,char splitSign = '|')
    293. {
    294. if(string.IsNullOrEmpty(valueStr))
    295. return null;
    296. string[] stringArray = valueStr.Split(splitSign);
    297. if(stringArray == null || stringArray.Length == 0)
    298. return null;
    299. Vector2[] vector2s = new Vector2[stringArray.Length];
    300. for(int i = 0; i < stringArray.Length; i++)
    301. {
    302. vector2s[i] = StringToVector2(stringArray[i]);
    303. }
    304. return vector2s;
    305. }
    306. public static Vector2Arr[] StringToVector2Array2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    307. {
    308. if(string.IsNullOrEmpty(valueStr))
    309. return null;
    310. string[] strArr1 = valueStr.Split(splitSign1);
    311. if(strArr1.Length == 0)
    312. return null;
    313. Vector2Arr[] arrArr = new Vector2Arr[strArr1.Length];
    314. for(int i = 0; i < strArr1.Length; i++)
    315. {
    316. arrArr[i] = new Vector2Arr()
    317. {
    318. array = StringToVector2Array(strArr1[i],splitSign2)
    319. };
    320. }
    321. return arrArr;
    322. }
    323. //vector3
    324. public static Vector3 StringToVector3(string valueStr,char splitSign = ',')
    325. {
    326. Vector3 value = Vector3.zero;
    327. if(!string.IsNullOrEmpty(valueStr))
    328. {
    329. string[] stringArray = valueStr.Split(splitSign);
    330. if(stringArray.Length >= 3)
    331. {
    332. value.x = StringToFloat(stringArray[0]);
    333. value.y = StringToFloat(stringArray[1]);
    334. value.z = StringToFloat(stringArray[2]);
    335. return value;
    336. }
    337. }
    338. Debug.LogWarning("String to Vector3 error");
    339. return value;
    340. }
    341. public static Vector3[] StringToVector3Array(string valueStr,char splitSign = '|')
    342. {
    343. if(string.IsNullOrEmpty(valueStr))
    344. return null;
    345. string[] stringArray = valueStr.Split(splitSign);
    346. if(stringArray == null || stringArray.Length == 0)
    347. return null;
    348. Vector3[] vector3s = new Vector3[stringArray.Length];
    349. for(int i = 0; i < stringArray.Length; i++)
    350. {
    351. vector3s[i] = StringToVector3(stringArray[i]);
    352. }
    353. return vector3s;
    354. }
    355. public static Vector3Arr[] StringToVector3Array2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    356. {
    357. if(string.IsNullOrEmpty(valueStr))
    358. return null;
    359. string[] strArr1 = valueStr.Split(splitSign1);
    360. if(strArr1.Length == 0)
    361. return null;
    362. Vector3Arr[] arrArr = new Vector3Arr[strArr1.Length];
    363. for(int i = 0; i < strArr1.Length; i++)
    364. {
    365. arrArr[i] = new Vector3Arr()
    366. {
    367. array = StringToVector3Array(strArr1[i],splitSign2)
    368. };
    369. }
    370. return arrArr;
    371. }
    372. //vector2Int
    373. public static Vector2Int StringToVector2Int(string valueStr,char splitSign = ',')
    374. {
    375. Vector2Int value = Vector2Int.zero;
    376. if(!string.IsNullOrEmpty(valueStr))
    377. {
    378. string[] stringArray = valueStr.Split(splitSign);
    379. if(stringArray != null && stringArray.Length >= 2)
    380. {
    381. value.x = StringToInt(stringArray[0]);
    382. value.y = StringToInt(stringArray[1]);
    383. return value;
    384. }
    385. }
    386. Debug.LogWarning("String to Vector2Int error");
    387. return value;
    388. }
    389. public static Vector2Int[] StringToVector2IntArray(string valueStr,char splitSign = '|')
    390. {
    391. if(string.IsNullOrEmpty(valueStr))
    392. return null;
    393. string[] stringArray = valueStr.Split(splitSign);
    394. if(stringArray == null || stringArray.Length == 0)
    395. return null;
    396. Vector2Int[] vector2Ints = new Vector2Int[stringArray.Length];
    397. for(int i = 0; i < stringArray.Length; i++)
    398. {
    399. vector2Ints[i] = StringToVector2Int(stringArray[i]);
    400. }
    401. return vector2Ints;
    402. }
    403. public static Vector2IntArr[] StringToVector2IntArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    404. {
    405. if(string.IsNullOrEmpty(valueStr))
    406. return null;
    407. string[] strArr1 = valueStr.Split(splitSign1);
    408. if(strArr1.Length == 0)
    409. return null;
    410. Vector2IntArr[] arrArr = new Vector2IntArr[strArr1.Length];
    411. for(int i = 0; i < strArr1.Length; i++)
    412. {
    413. arrArr[i] = new Vector2IntArr()
    414. {
    415. array = StringToVector2IntArray(strArr1[i],splitSign2)
    416. };
    417. }
    418. return arrArr;
    419. }
    420. //vector3Int
    421. public static Vector3Int StringToVector3Int(string valueStr,char splitSign = ',')
    422. {
    423. Vector3Int value = Vector3Int.zero;
    424. if(!string.IsNullOrEmpty(valueStr))
    425. {
    426. string[] stringArray = valueStr.Split(splitSign);
    427. if(stringArray.Length >= 3)
    428. {
    429. value.x = StringToInt(stringArray[0]);
    430. value.y = StringToInt(stringArray[1]);
    431. value.z = StringToInt(stringArray[2]);
    432. return value;
    433. }
    434. }
    435. Debug.LogWarning("String to Vector3 error");
    436. return value;
    437. }
    438. public static Vector3Int[] StringToVector3IntArray(string valueStr,char splitSign = '|')
    439. {
    440. if(string.IsNullOrEmpty(valueStr))
    441. return null;
    442. string[] stringArray = valueStr.Split(splitSign);
    443. if(stringArray == null || stringArray.Length == 0)
    444. return null;
    445. Vector3Int[] vector3Ints = new Vector3Int[stringArray.Length];
    446. for(int i = 0; i < stringArray.Length; i++)
    447. {
    448. vector3Ints[i] = StringToVector3Int(stringArray[i]);
    449. }
    450. return vector3Ints;
    451. }
    452. public static Vector3IntArr[] StringToVector3IntArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    453. {
    454. if(string.IsNullOrEmpty(valueStr))
    455. return null;
    456. string[] strArr1 = valueStr.Split(splitSign1);
    457. if(strArr1.Length == 0)
    458. return null;
    459. Vector3IntArr[] arrArr = new Vector3IntArr[strArr1.Length];
    460. for(int i = 0; i < strArr1.Length; i++)
    461. {
    462. arrArr[i] = new Vector3IntArr()
    463. {
    464. array = StringToVector3IntArray(strArr1[i],splitSign2)
    465. };
    466. }
    467. return arrArr;
    468. }
    469. //color
    470. public static Color StringToColor(string valueStr,char splitSign = ',')
    471. {
    472. if(string.IsNullOrEmpty(valueStr))
    473. return Color.white;
    474. string[] stringArray = valueStr.Split(splitSign);
    475. if(stringArray.Length < 3)
    476. return Color.white;
    477. Color color = new Color()
    478. {
    479. r = StringToFloat(stringArray[0]),
    480. g = StringToFloat(stringArray[1]),
    481. b = StringToFloat(stringArray[2]),
    482. a = stringArray.Length < 4 ? 1 : StringToFloat(stringArray[3])
    483. };
    484. return color;
    485. }
    486. public static Color32 StringToColor32(string valueStr,char splitSign = ',')
    487. {
    488. if(string.IsNullOrEmpty(valueStr))
    489. return Color.white;
    490. string[] stringArray = valueStr.Split(splitSign);
    491. if(stringArray.Length < 3)
    492. return Color.white;
    493. Color32 color = new Color32()
    494. {
    495. r = StringToByte(stringArray[0]),
    496. g = StringToByte(stringArray[1]),
    497. b = StringToByte(stringArray[2]),
    498. a = stringArray.Length < 4 ? (byte)1 : StringToByte(stringArray[3])
    499. };
    500. return color;
    501. }
    502. public static Color[] StringToColorArray(string valueStr,char splitSign = '|')
    503. {
    504. if(string.IsNullOrEmpty(valueStr))
    505. return null;
    506. string[] stringArray = valueStr.Split(splitSign);
    507. if(stringArray == null || stringArray.Length == 0)
    508. return null;
    509. Color[] colors = new Color[stringArray.Length];
    510. for(int i = 0; i < stringArray.Length; i++)
    511. {
    512. colors[i] = StringToColor(stringArray[i]);
    513. }
    514. return colors;
    515. }
    516. public static Color32[] StringToColor32Array(string valueStr,char splitSign = '|')
    517. {
    518. if(string.IsNullOrEmpty(valueStr))
    519. return null;
    520. string[] stringArray = valueStr.Split(splitSign);
    521. if(stringArray == null || stringArray.Length == 0)
    522. return null;
    523. Color32[] colors = new Color32[stringArray.Length];
    524. for(int i = 0; i < stringArray.Length; i++)
    525. {
    526. colors[i] = StringToColor32(stringArray[i]);
    527. }
    528. return colors;
    529. }
    530. public static ColorArr[] StringToColorArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    531. {
    532. if(string.IsNullOrEmpty(valueStr))
    533. return null;
    534. string[] strArr1 = valueStr.Split(splitSign1);
    535. if(strArr1.Length == 0)
    536. return null;
    537. ColorArr[] arrArr = new ColorArr[strArr1.Length];
    538. for(int i = 0; i < strArr1.Length; i++)
    539. {
    540. arrArr[i] = new ColorArr()
    541. {
    542. array = StringToColorArray(strArr1[i],splitSign2)
    543. };
    544. }
    545. return arrArr;
    546. }
    547. public static Color32Arr[] StringToColor32Array2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
    548. {
    549. if(string.IsNullOrEmpty(valueStr))
    550. return null;
    551. string[] strArr1 = valueStr.Split(splitSign1);
    552. if(strArr1.Length == 0)
    553. return null;
    554. Color32Arr[] arrArr = new Color32Arr[strArr1.Length];
    555. for(int i = 0; i < strArr1.Length; i++)
    556. {
    557. arrArr[i] = new Color32Arr()
    558. {
    559. array = StringToColor32Array(strArr1[i],splitSign2)
    560. };
    561. }
    562. return arrArr;
    563. }
    564. #endregion
    565. #region MyRegion
    566. public static string GetRandomString(int length)
    567. {
    568. StringBuilder builder = new StringBuilder();
    569. string abc = "abcdefghijklmnopqrstuvwxyzo0123456789QWERTYUIOPASDFGHJKLZXCCVBMN";
    570. for(int i = 0; i < length; i++)
    571. {
    572. builder.Append(abc[UnityEngine.Random.Range(0,abc.Length - 1)]);
    573. }
    574. return builder.ToString();
    575. }
    576. public static string Join<T>(T[] arr,string join = ",")
    577. {
    578. if(arr == null || arr.Length == 0)
    579. return null;
    580. StringBuilder builder = new StringBuilder();
    581. for(int i = 0; i < arr.Length; i++)
    582. {
    583. builder.Append(arr[i]);
    584. if(i < arr.Length - 1)
    585. builder.Append(join);
    586. }
    587. return builder.ToString();
    588. }
    589. /// <summary>
    590. /// 中文逗号转英文逗号
    591. /// </summary>
    592. /// <param name="input"></param>
    593. /// <returns></returns>
    594. public static string ToDBC(string input)
    595. {
    596. char[] c = input.ToCharArray();
    597. for(int i = 0; i < c.Length; i++)
    598. {
    599. if(c[i] == 12288)
    600. {
    601. c[i] = (char)32;
    602. continue;
    603. }
    604. if(c[i] > 65280 && c[i] < 65375)
    605. c[i] = (char)(c[i] - 65248);
    606. }
    607. return new string(c);
    608. }
    609. /// <summary>
    610. /// 字符转 ascii 码
    611. /// </summary>
    612. /// <param name="character"></param>
    613. /// <returns></returns>
    614. public static int Asc(string character)
    615. {
    616. if(character.Length == 1)
    617. {
    618. System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
    619. int intAsciiCode = (int)asciiEncoding.GetBytes(character)[0];
    620. return (intAsciiCode);
    621. }
    622. Debug.LogError("Character is not valid.");
    623. return -1;
    624. }
    625. /// <summary>
    626. /// ascii码转字符
    627. /// </summary>
    628. /// <param name="asciiCode"></param>
    629. /// <returns></returns>
    630. public static string Chr(int asciiCode)
    631. {
    632. if(asciiCode >= 0 && asciiCode <= 255)
    633. {
    634. System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
    635. byte[] byteArray = new byte[] { (byte)asciiCode };
    636. string strCharacter = asciiEncoding.GetString(byteArray);
    637. return (strCharacter);
    638. }
    639. Debug.LogError("ASCII Code is not valid.");
    640. return string.Empty;
    641. }
    642. /// <summary>
    643. /// 过滤掉表情符号
    644. /// </summary>
    645. /// <returns>The emoji.</returns>
    646. /// <param name="str">String.</param>
    647. public static string FilterEmoji(string str)
    648. {
    649. List<string> patten = new List<string>() { @"\p{Cs}",@"\p{Co}",@"\p{Cn}",@"[\u2702-\u27B0]" };
    650. for(int i = 0; i < patten.Count; i++)
    651. {
    652. str = Regex.Replace(str,patten[i],"");//屏蔽emoji
    653. }
    654. return str;
    655. }
    656. /// <summary>
    657. /// 过滤掉表情符号
    658. /// </summary>
    659. /// <returns>The emoji.</returns>
    660. /// <param name="str">String.</param>
    661. public static bool IsFilterEmoji(string str)
    662. {
    663. bool bEmoji = false;
    664. List<string> patten = new List<string>() { @"\p{Cs}",@"\p{Co}",@"\p{Cn}",@"[\u2702-\u27B0]" };
    665. for(int i = 0; i < patten.Count; i++)
    666. {
    667. bEmoji = Regex.IsMatch(str,patten[i]);
    668. if(bEmoji)
    669. {
    670. break;
    671. }
    672. }
    673. return bEmoji;
    674. }
    675. #endregion
    676. #region StringObjectDictionaryExtensions
    677. /// <summary>
    678. /// 针对字典中包含以下键值进行结构:mctid0=xxx;mccount0=1,mctid1=kn2,mccount=2。将其前缀去掉,数字后缀变为键,如{后缀,(去掉前后缀的键,值)},注意后缀可能是空字符串即没有后缀
    679. /// </summary>
    680. /// <param name="dic"></param>
    681. /// <param name="prefix">前缀,可以是空引用或空字符串,都表示没有前缀。</param>
    682. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    683. public static IEnumerable<IGrouping<string,(string, object)>> GetValuesWithoutPrefix(this IReadOnlyDictionary<string,object> dic,string prefix = null)
    684. {
    685. //prefix ??= string.Empty;
    686. prefix = prefix ?? string.Empty;
    687. var coll = from tmp in dic.Where(c => c.Key.StartsWith(prefix)) //仅针对指定前缀的键值
    688. let p3 = tmp.Key.Get3Segment(prefix)
    689. group (p3.Item2, tmp.Value) by p3.Item3;
    690. return coll;
    691. }
    692. /// <summary>
    693. /// 分解字符串为三段,前缀,词根,数字后缀(字符串形式)。
    694. /// </summary>
    695. /// <param name="str"></param>
    696. /// <param name="prefix">前缀,可以是空引用或空字符串,都表示没有前缀。</param>
    697. /// <returns></returns>
    698. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    699. public static (string, string, string) Get3Segment(this string str,string prefix = null)
    700. {
    701. //prefix ??= string.Empty;
    702. prefix = prefix ?? string.Empty;
    703. //最后十进制数字尾串的长度
    704. int suffixLen = Enumerable.Reverse(str).TakeWhile(c => char.IsDigit(c)).Count();
    705. //获取十进制数字后缀
    706. //string suufix = str[^suffixLen..]; //^suffixLen:倒序下标;suffixLen..:从指定位置开始直到末尾
    707. string suufix = str.Substring(str.Length - suffixLen);
    708. //return (prefix, str[prefix.Length..^suufix.Length], suufix);
    709. string middle = str.Substring(prefix.Length,str.Length - prefix.Length - suufix.Length);
    710. return (prefix, middle, suufix);
    711. }
    712. #endregion
    713. }

    Demo链接:

    【Unity】升级版·Excel数据解析,自动创建C#类,自动创建ScriptableObject生成类,自动序列化Asset-Unity3D文档类资源-CSDN下载Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化A更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/qq_39108767/85913988

  • 相关阅读:
    徐建鸿:深耕中医康养的“托钵行者”
    【PHP】麻醉临床信息系统
    Java反应式编程(1)
    Vue-中央事件总线(bus)
    尚硅谷ES6复习总结上(64th)
    计算机毕业设计Java汉服服装租赁系统(源码+系统+mysql数据库+lw文档)
    【php学习笔记】文件系统---制作备忘录和修改配置文件
    机器学习笔记之贝叶斯线性回归(三)预测任务推导过程
    使用serverless超低成本快速部署开源项目
    Windows-Oracle11g 安装详解-含Navicate远程连接配置 -本地监听设置及更换navicate环境指向的oci.dll
  • 原文地址:https://blog.csdn.net/qq_39108767/article/details/125608062