feat(refactor): introduce configuration-based Excel table processing
- Replace command-line options with single tables file configuration - Add TablesConfig class for parsing table configuration from Excel - Add TableEntry class for storing table configuration data - Change GetCellValue method visibility from static to internal static - Simplify Program.cs command line interface with single --tables option - Add error handling for missing files and directories - Update README with new usage instructions and examples - Remove deprecated command-line option descriptions
This commit is contained in:
@@ -147,7 +147,7 @@ namespace ExcelTool
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetCellValue(ICell cell)
|
internal static string GetCellValue(ICell cell)
|
||||||
{
|
{
|
||||||
if (cell == null)
|
if (cell == null)
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
+55
-50
@@ -12,35 +12,16 @@ namespace ExcelTool
|
|||||||
{
|
{
|
||||||
static async Task<int> Main(string[] args)
|
static async Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
Option<DirectoryInfo> inputOption = new(
|
Option<FileInfo> tablesOption = new(
|
||||||
name: "--input",
|
name: "--tables",
|
||||||
description: "Excel 文件所在目录",
|
description: "__tables__.xlsx 的路径",
|
||||||
getDefaultValue: () => new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory)
|
getDefaultValue: () => new FileInfo(AppDomain.CurrentDomain.BaseDirectory)
|
||||||
);
|
|
||||||
|
|
||||||
Option<DirectoryInfo> outputCodeDirOption = new(
|
|
||||||
name: "--outputCodeDir",
|
|
||||||
description: "CS 模板代码输出目录"
|
|
||||||
);
|
|
||||||
|
|
||||||
Option<DirectoryInfo> outputDataDirOption = new(
|
|
||||||
name: "--outputDataDir",
|
|
||||||
description: "二进制数据输出目录"
|
|
||||||
);
|
|
||||||
|
|
||||||
Option<string> namespaceOption = new(
|
|
||||||
name: "--namespace",
|
|
||||||
description: "生成代码的命名空间",
|
|
||||||
getDefaultValue: () => ""
|
|
||||||
);
|
);
|
||||||
|
|
||||||
RootCommand rootCommand = new();
|
RootCommand rootCommand = new();
|
||||||
rootCommand.AddOption(inputOption);
|
rootCommand.AddOption(tablesOption);
|
||||||
rootCommand.AddOption(outputCodeDirOption);
|
|
||||||
rootCommand.AddOption(outputDataDirOption);
|
|
||||||
rootCommand.AddOption(namespaceOption);
|
|
||||||
|
|
||||||
rootCommand.SetHandler(ExcelProcess.Run, inputOption, outputCodeDirOption, outputDataDirOption, namespaceOption);
|
rootCommand.SetHandler(ExcelProcess.Run, tablesOption);
|
||||||
// TODO 单元测试
|
// TODO 单元测试
|
||||||
|
|
||||||
return await rootCommand.InvokeAsync(args);
|
return await rootCommand.InvokeAsync(args);
|
||||||
@@ -49,18 +30,21 @@ namespace ExcelTool
|
|||||||
|
|
||||||
static class ExcelProcess
|
static class ExcelProcess
|
||||||
{
|
{
|
||||||
public static void Run(DirectoryInfo inputDir, DirectoryInfo? outputCodeDir, DirectoryInfo? outputDataDir,
|
public static void Run(FileInfo tablesFile)
|
||||||
string nameSpace)
|
|
||||||
{
|
{
|
||||||
string path = inputDir.FullName;
|
if(!tablesFile.Exists)
|
||||||
string codeDir = outputCodeDir?.FullName ?? outputDataDir?.FullName ?? path;
|
{
|
||||||
string dataDir = outputDataDir?.FullName ?? outputCodeDir?.FullName ?? path;
|
$"未在 {tablesFile} 处找到文件!".WriteErrorLine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DirectoryInfo dirInfo = new(path);
|
|
||||||
FileInfo[] excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
string path = tablesFile.FullName;
|
||||||
if (excels.Length <= 0)
|
List<TableEntry> tableEntries = TablesConfig.Parse(path);
|
||||||
|
|
||||||
|
if (tableEntries.Count <= 0)
|
||||||
{
|
{
|
||||||
"当前exe目录或者目标目录没有excels文件,请重新设置目录".WriteErrorLine();
|
"__tables__.xlsx内没有数据".WriteErrorLine();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -69,55 +53,76 @@ namespace ExcelTool
|
|||||||
"== 说明:将exe放在xlsx目录中或者exe或者传入根目录 ==".WriteSuccessLine();
|
"== 说明:将exe放在xlsx目录中或者exe或者传入根目录 ==".WriteSuccessLine();
|
||||||
"==========================================================".WriteSuccessLine();
|
"==========================================================".WriteSuccessLine();
|
||||||
|
|
||||||
excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
// excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
||||||
|
|
||||||
//读取
|
//读取
|
||||||
foreach (FileInfo file in excels)
|
foreach (TableEntry tableEntry in tableEntries)
|
||||||
{
|
{
|
||||||
if (file.Name.StartsWith("~$")) continue;
|
if (!Path.Exists(tableEntry.InputFile))
|
||||||
|
{
|
||||||
|
$"{tableEntry.InputFile} 不存在!".WriteWarningLine();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
List<ParsedSheet> sheets = ExcelHelper.ParseAllSheets(file.FullName, out List<ParsedEnum>? enumSheets);
|
if (!Path.Exists(tableEntry.OutputCodeDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(tableEntry.OutputCodeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Path.Exists(tableEntry.OutputDataDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(tableEntry.OutputDataDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
string fileName = Path.GetFileName(tableEntry.InputFile);
|
||||||
|
|
||||||
|
List<ParsedSheet> sheets = ExcelHelper.ParseAllSheets(tableEntry.InputFile, out List<ParsedEnum>? enumSheets);
|
||||||
|
|
||||||
//生成CS文件
|
//生成CS文件
|
||||||
bool res = GenModels.GenCSharpModel(sheets, codeDir, nameSpace);
|
bool res = GenModels.GenCSharpModel(sheets, tableEntry.OutputCodeDir, tableEntry.Namespace);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
$"{file.Name}CS模板生成成功".WriteSuccessLine();
|
$"{fileName}CS模板生成成功".WriteSuccessLine();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$"{file.Name}CS模板生成失败".WriteErrorLine();
|
$"{fileName}CS模板生成失败".WriteErrorLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成CS枚举文件
|
// 生成CS枚举文件
|
||||||
if (enumSheets.Count > 0)
|
if (enumSheets.Count > 0)
|
||||||
{
|
{
|
||||||
bool enumRes = GenEnums.GenCSharpEnum(enumSheets, codeDir, nameSpace);
|
bool enumRes = GenEnums.GenCSharpEnum(enumSheets, tableEntry.OutputCodeDir, tableEntry.Namespace);
|
||||||
if (enumRes)
|
if (enumRes)
|
||||||
$"{file.Name} 枚举代码生成成功".WriteSuccessLine();
|
$"{fileName} 枚举代码生成成功".WriteSuccessLine();
|
||||||
else
|
else
|
||||||
$"{file.Name} 枚举代码生成失败".WriteErrorLine();
|
$"{fileName} 枚举代码生成失败".WriteErrorLine();
|
||||||
|
|
||||||
bool enumIdsRes = GenEnums.GenEnumIds(enumSheets, codeDir, nameSpace);
|
bool enumIdsRes = GenEnums.GenEnumIds(enumSheets, tableEntry.OutputCodeDir, tableEntry.Namespace);
|
||||||
if (enumIdsRes)
|
if (enumIdsRes)
|
||||||
$"{file.Name} EnumIds 生成成功".WriteSuccessLine();
|
$"{fileName} EnumIds 生成成功".WriteSuccessLine();
|
||||||
else
|
else
|
||||||
$"{file.Name} EnumIds 生成失败".WriteErrorLine();
|
$"{fileName} EnumIds 生成失败".WriteErrorLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
//生成二进制文件,如果list或者vector数据为空则写入0,要根据类型来读取csv的字段数据强转成对应的数据类型然后写入
|
//生成二进制文件,如果list或者vector数据为空则写入0,要根据类型来读取csv的字段数据强转成对应的数据类型然后写入
|
||||||
res = TableExcelExportBytes.ExportToFile(sheets, dataDir);
|
res = TableExcelExportBytes.ExportToFile(sheets, tableEntry.OutputDataDir);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
$"{file.Name}二进制数据生成成功".WriteSuccessLine();
|
$"{fileName}二进制数据生成成功".WriteSuccessLine();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$"{file.Name}二进制数据生成失败".WriteErrorLine();
|
$"{fileName}二进制数据生成失败".WriteErrorLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Test()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using NPOI.SS.UserModel;
|
||||||
|
using NPOI.XSSF.UserModel;
|
||||||
|
|
||||||
|
namespace ExcelTool;
|
||||||
|
|
||||||
|
public static class TablesConfig
|
||||||
|
{
|
||||||
|
internal static List<TableEntry> Parse(string tablesXlsxPath)
|
||||||
|
{
|
||||||
|
List<TableEntry> tableEntries = [];
|
||||||
|
|
||||||
|
string tablesDir = Path.GetDirectoryName(Path.GetFullPath(tablesXlsxPath))!;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using FileStream fs = File.OpenRead(tablesXlsxPath);
|
||||||
|
XSSFWorkbook workbook = new(fs);
|
||||||
|
|
||||||
|
ISheet sheet = workbook.GetSheetAt(0);
|
||||||
|
|
||||||
|
for (int i = 4; i <= sheet.LastRowNum; i++)
|
||||||
|
{
|
||||||
|
IRow row = sheet.GetRow(i);
|
||||||
|
if (row == null || string.IsNullOrEmpty(ExcelHelper.GetCellValue(row.GetCell(0)))) continue;
|
||||||
|
|
||||||
|
TableEntry tableEntry = new()
|
||||||
|
{
|
||||||
|
InputFile = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(0))),
|
||||||
|
Namespace = ExcelHelper.GetCellValue(row.GetCell(1)),
|
||||||
|
OutputCodeDir = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(2))),
|
||||||
|
OutputDataDir = ResolveDir(ExcelHelper.GetCellValue(row.GetCell(3))),
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
tableEntries.Add(tableEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
exception.ToString().WriteErrorLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableEntries;
|
||||||
|
|
||||||
|
string ResolveDir(string raw)
|
||||||
|
{
|
||||||
|
if (raw != null)
|
||||||
|
{
|
||||||
|
return Path.IsPathRooted(raw)
|
||||||
|
? raw
|
||||||
|
: Path.GetFullPath(Path.Combine(tablesDir, raw));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TableEntry
|
||||||
|
{
|
||||||
|
public string InputFile { get; init; } // e.g. "AudioConfigs.xlsx"
|
||||||
|
public string Namespace { get; init; } // e.g. "OCES.Audio"
|
||||||
|
public string OutputCodeDir { get; init; }
|
||||||
|
public string OutputDataDir { get; init; }
|
||||||
|
}
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
# ExcelTool(Unity打表工具)
|
# ExcelTool
|
||||||
|
|
||||||
通用Excel打表工具
|
|
||||||
|
|
||||||
#### 前言
|
|
||||||
|
|
||||||
目前项目中用的csv转json工具,会带来严重的gc性能问题,例如启动加载慢,影响帧率等问题,转成的json数据,我们需要手动写对应的解析模板代码,所以针对以上问题,本通用打表工具应运而生,用二进制数据替代json数据会比较好的解决性能问题,而且能够自动生成对应的模板代码,节省了写重复代码的时间,而且二进制数据比json读取速度更快,文件大小更小。
|
|
||||||
|
|
||||||
|
本项目基于 https://github.com/dingxiaowei/ExcelTool 开发。用于 [AudioSystem](https://github.com/htw128/UnityAudioExtend), [HapticSystem](https://github.com/htw128/UnityAudioExtend#Haptic) 导出数据使用。
|
||||||
|
|
||||||
#### 功能
|
#### 功能
|
||||||
|
|
||||||
@@ -22,16 +17,9 @@
|
|||||||
* 支持自定义字段 vector,例如[1,2,3]
|
* 支持自定义字段 vector,例如[1,2,3]
|
||||||
* 支持自定义字段 list,其实是vector数组,例如[[1,2,3],[2,3,4],[4,5,6]]
|
* 支持自定义字段 list,其实是vector数组,例如[[1,2,3],[2,3,4],[4,5,6]]
|
||||||
|
|
||||||
#### 效果
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
相比较json,二进制文件大小只有其1/4
|
|
||||||
|
|
||||||
##### 自动生成的模板代码
|
##### 自动生成的模板代码
|
||||||
|
|
||||||
```
|
```c#
|
||||||
/*
|
/*
|
||||||
* auto generated by tools(注意:千万不要手动修改本文件)
|
* auto generated by tools(注意:千万不要手动修改本文件)
|
||||||
* avatarguideTest
|
* avatarguideTest
|
||||||
@@ -197,7 +185,7 @@ public partial class avatarguideTestConfig : IBinarySerializable
|
|||||||
|
|
||||||
##### 解析二进制文件
|
##### 解析二进制文件
|
||||||
|
|
||||||
```
|
```c#
|
||||||
IBinarySerializable newavList = new avatarguideTestList();
|
IBinarySerializable newavList = new avatarguideTestList();
|
||||||
var readOK = FileManager.ReadBinaryDataFromFile(Path.Combine(path, "avatarguideTest.dat"), ref newavList);
|
var readOK = FileManager.ReadBinaryDataFromFile(Path.Combine(path, "avatarguideTest.dat"), ref newavList);
|
||||||
if (readOK)
|
if (readOK)
|
||||||
@@ -216,7 +204,7 @@ else
|
|||||||
|
|
||||||
将二进制文件(后缀必须是.bytes)放在Resources目录,然后通过Resources.Load接口加载
|
将二进制文件(后缀必须是.bytes)放在Resources目录,然后通过Resources.Load接口加载
|
||||||
|
|
||||||
```
|
```c#
|
||||||
var bytes = Resources.Load<TextAsset>("avatarguideTest");
|
var bytes = Resources.Load<TextAsset>("avatarguideTest");
|
||||||
if (bytes == null)
|
if (bytes == null)
|
||||||
{
|
{
|
||||||
@@ -242,7 +230,7 @@ else
|
|||||||
|
|
||||||
#### 泛型读表
|
#### 泛型读表
|
||||||
|
|
||||||
```
|
```c#
|
||||||
T ReadTable<T>(string tableName) where T : IBinarySerializable, new()
|
T ReadTable<T>(string tableName) where T : IBinarySerializable, new()
|
||||||
{
|
{
|
||||||
var bytes = LoadTextAsset(tableName);
|
var bytes = LoadTextAsset(tableName);
|
||||||
@@ -265,7 +253,7 @@ return default(T);
|
|||||||
|
|
||||||
##### 查询数据
|
##### 查询数据
|
||||||
|
|
||||||
```
|
```c#
|
||||||
var avatarguideTest = (avatarguideTestConfig)newavList;
|
var avatarguideTest = (avatarguideTestConfig)newavList;
|
||||||
var obj = avatarguideTest.QueryById(2);
|
var obj = avatarguideTest.QueryById(2);
|
||||||
if (obj != null && obj.Count() > 0)
|
if (obj != null && obj.Count() > 0)
|
||||||
@@ -274,7 +262,7 @@ if (obj != null && obj.Count() > 0)
|
|||||||
|
|
||||||
#### 自动化拷贝批处理
|
#### 自动化拷贝批处理
|
||||||
|
|
||||||
```
|
```batch
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
echo "开始拷贝jsons"
|
echo "开始拷贝jsons"
|
||||||
@@ -292,21 +280,17 @@ pause
|
|||||||
|
|
||||||
上面的config类采用的是partical class 就是为了方便扩展自定义的方法,如果想要添加自定义方法也定义一个partical class文件作为扩展即可
|
上面的config类采用的是partical class 就是为了方便扩展自定义的方法,如果想要添加自定义方法也定义一个partical class文件作为扩展即可
|
||||||
|
|
||||||
#### 待扩展的功能
|
#### TODO
|
||||||
|
|
||||||
目前还不是最理想的状态,还有很多可以扩展完善的功能,如下:
|
|
||||||
|
|
||||||
* [x] 支持枚举类型
|
* [x] 支持枚举类型
|
||||||
* [ ] 支持生成各种类型的文件,例如json、xml、proto、lua等
|
* [ ] 支持不同表不同namespace
|
||||||
* [ ] 支持生成各种语法的模板代码
|
|
||||||
* [ ] 支持Excel数据配置规范性检测,例如手误配置不符合规范导致加载异常,例如大小写逗号(肉眼容易忽略),或者空格等等
|
* [ ] 支持Excel数据配置规范性检测,例如手误配置不符合规范导致加载异常,例如大小写逗号(肉眼容易忽略),或者空格等等
|
||||||
* [ ] 支持Excel字段客户端服务器可选项
|
* [ ] ID不能重复
|
||||||
|
* [ ] MusicContainer不能自引用
|
||||||
|
* [ ] 同时播放的音乐BPM必须一致
|
||||||
|
* [ ] 生成CueSheet避免magic number
|
||||||
* [ ] 支持更多自定义数据类型扩展
|
* [ ] 支持更多自定义数据类型扩展
|
||||||
|
|
||||||
#### 代码
|
|
||||||
|
|
||||||
https://github.com/dingxiaowei/ExcelTool 喜欢麻烦点个star吧
|
|
||||||
|
|
||||||
#### Unity客户端使用范例
|
#### Unity客户端使用范例
|
||||||
|
|
||||||
采用的美术效果机型适配商业化解决方案Demo
|
采用的美术效果机型适配商业化解决方案Demo
|
||||||
|
|||||||
Reference in New Issue
Block a user