refactor: 优化 Excel 解析流程与代码生成健壮性
- 新增 ParsedSheet 类统一承载单个 sheet 的解析结果(SheetName / Headers / Data) - ExcelHelper 新增 ParseAllSheets(),一次打开文件完成所有 sheet 解析, 消除原先 ExcelHeaders + ExcelData 重复打开同一文件的问题 - Program.cs 在遍历层面调用 ParseAllSheets(),解析结果共享给 GenCSharpModel 和 ExportToFile,每个 xlsx 文件只打开一次 - GenModels / TableExcelExportBytes 签名改为接收 List<ParsedSheet> - 修复 Program.cs 中遍历 excels 时 return 应为 continue 的 bug, 避免遇到 ~$ 临时文件时提前退出 - TypeDescriptor 新增 ReadExpression 属性,GetReadExpr 改为直接读取该属性, 消除原先通过字符串截取反推读取表达式的脆弱实现 - FileManager.WriteToFile 默认编码由 Encoding.Default 改为 UTF8(无 BOM), 确保生成的 .cs 文件跨平台编码一致 - 移除 FileManager.CreateDir 中无条件删除已存在目录的危险逻辑 - 移除 CsvHelper 中未被调用的 CSVHeader / GenBinaryData 死代码, StreamReader / FileStream 改为 using 简写形式 - 清理 Program.cs 末尾遗留的注释测试代码和无效语句,补充 TODO 标记
This commit is contained in:
@@ -1,235 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Data;
|
|
||||||
using Spire.Xls;
|
|
||||||
|
|
||||||
namespace ExcelTool
|
|
||||||
{
|
|
||||||
public class CsvHelper
|
|
||||||
{
|
|
||||||
static DataTable CSVHeader(string fileName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DataTable dt = new DataTable();
|
|
||||||
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
|
||||||
StreamReader sr = new StreamReader(fs, Encoding.Default);
|
|
||||||
string strLine = "";
|
|
||||||
string[] aryLine;
|
|
||||||
int columnCount = 0;
|
|
||||||
int lineIndex = 0;
|
|
||||||
while ((strLine = sr.ReadLine()) != null)
|
|
||||||
{
|
|
||||||
aryLine = strLine.Replace("\"", "").Replace(" ", "").Split(',');
|
|
||||||
if (lineIndex == 0)
|
|
||||||
{
|
|
||||||
columnCount = aryLine.Length;
|
|
||||||
for (int i = 0; i < columnCount; i++)
|
|
||||||
{
|
|
||||||
int typeIndex = i * 2;
|
|
||||||
int nameIndex = typeIndex + 1;
|
|
||||||
if (nameIndex < columnCount)
|
|
||||||
{
|
|
||||||
DataColumn dc = new DataColumn($"{aryLine[nameIndex]}|{aryLine[typeIndex]}");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dt.Columns.Add(dc);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sr.Close();
|
|
||||||
fs.Close();
|
|
||||||
sr.Dispose();
|
|
||||||
fs.Dispose();
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataTable CSV2DataTable(string fileName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DataTable dt = new DataTable();
|
|
||||||
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
|
||||||
StreamReader sr = new StreamReader(fs, Encoding.Default);
|
|
||||||
string strLine = "";
|
|
||||||
string[] aryLine;
|
|
||||||
int columnCount = 0;
|
|
||||||
int lineIndex = 0;
|
|
||||||
while ((strLine = sr.ReadLine()) != null)
|
|
||||||
{
|
|
||||||
aryLine = strLine.Replace("\"", "").Replace(" ", "").Split(',');
|
|
||||||
if (lineIndex == 0)
|
|
||||||
{
|
|
||||||
columnCount = aryLine.Length;
|
|
||||||
for (int i = 0; i < columnCount; i++)
|
|
||||||
{
|
|
||||||
int typeIndex = i * 2;
|
|
||||||
int nameIndex = typeIndex + 1;
|
|
||||||
if (nameIndex < columnCount)
|
|
||||||
{
|
|
||||||
DataColumn dc = new DataColumn($"{aryLine[nameIndex]}|{aryLine[typeIndex]}");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dt.Columns.Add(dc);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
columnCount /= 2;
|
|
||||||
}
|
|
||||||
else if (lineIndex == 1) // 注释行
|
|
||||||
{
|
|
||||||
// 注释行先不读,因为注释行里有,号无法分割
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//1,gender1,12.8,TRUE,"[0,0,1]","[[0,0,0],[0,1,0],[0,2,0],[0,4,0]]"
|
|
||||||
List<string> strs = new List<string>();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
bool flag = false;
|
|
||||||
for (int i = 0; i < strLine.Length; i++)
|
|
||||||
{
|
|
||||||
if (strLine[i] == '"')
|
|
||||||
{
|
|
||||||
flag = !flag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flag || (!flag && strLine[i] != ','))
|
|
||||||
{
|
|
||||||
sb.Append(strLine[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strs.Add(sb.ToString());
|
|
||||||
sb.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sb.Length > 0 || strs.Count < columnCount)
|
|
||||||
{
|
|
||||||
strs.Add(sb.ToString());
|
|
||||||
sb.Clear();
|
|
||||||
}
|
|
||||||
aryLine = strs.ToArray();
|
|
||||||
DataRow dr = dt.NewRow();
|
|
||||||
for (int j = 0; j < columnCount; j++)
|
|
||||||
{
|
|
||||||
dr[j] = aryLine[j];
|
|
||||||
}
|
|
||||||
dt.Rows.Add(dr);
|
|
||||||
}
|
|
||||||
lineIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
sr.Close();
|
|
||||||
fs.Close();
|
|
||||||
sr.Dispose();
|
|
||||||
fs.Dispose();
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GenBinaryData(string fileName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FileInfo fileInfo = new FileInfo(fileName);
|
|
||||||
var csvName = fileInfo.Name.Remove(fileInfo.Name.IndexOf(".csv"));
|
|
||||||
|
|
||||||
//先写入行数,然后没一行的数据一次写入 小写类型、字符串
|
|
||||||
List<Tuple<string, string>> datas = new List<Tuple<string, string>>();
|
|
||||||
var dataTable = CSV2DataTable(fileName);
|
|
||||||
Tuple<string, string> rowCount = new Tuple<string, string>("int", dataTable.Rows.Count.ToString());
|
|
||||||
datas.Add(rowCount);
|
|
||||||
//foreach (var col in dataTable.Columns)
|
|
||||||
//{
|
|
||||||
// ConsoleHelper.WriteInfoLine(col.ToString());
|
|
||||||
//}
|
|
||||||
//for (int i = 0; i < dataTable.Columns.Count; i++)
|
|
||||||
//{
|
|
||||||
// ConsoleHelper.WriteInfoLine(dataTable.Columns[i].ToString());
|
|
||||||
//}
|
|
||||||
foreach (DataRow row in dataTable.Rows)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < dataTable.Columns.Count; i++)
|
|
||||||
{
|
|
||||||
var typeHeader = dataTable.Columns[i].ToString().ToLower();
|
|
||||||
var headers = typeHeader.Split('|');
|
|
||||||
Tuple<string, string> t = new Tuple<string, string>(headers[1], row[i].ToString());
|
|
||||||
datas.Add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var binaryFilePath = Path.Combine(fileInfo.DirectoryName, $"{csvName}.bytes");
|
|
||||||
FileManager.WriteBinaryDatasToFile(binaryFilePath, datas);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CsvToXlsx(string fileName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FileInfo fileInfo = new FileInfo(fileName);
|
|
||||||
var filePath = fileInfo.DirectoryName;
|
|
||||||
var csvName = fileInfo.Name.Remove(fileInfo.Name.IndexOf(".csv"));
|
|
||||||
|
|
||||||
//加载CSV文件
|
|
||||||
Workbook workbook = new Workbook();
|
|
||||||
workbook.LoadFromFile(fileName, ",", 1, 1);
|
|
||||||
|
|
||||||
//获取第一个工作表
|
|
||||||
Worksheet sheet = workbook.Worksheets[0];
|
|
||||||
|
|
||||||
//访问工作表中使用的范围
|
|
||||||
CellRange usedRange = sheet.AllocatedRange;
|
|
||||||
//当将范围内的数字保存为文本时,忽略错误
|
|
||||||
usedRange.IgnoreErrorOptions = IgnoreErrorType.NumberAsText;
|
|
||||||
//自适应行高、列宽
|
|
||||||
usedRange.AutoFitColumns();
|
|
||||||
usedRange.AutoFitRows();
|
|
||||||
|
|
||||||
var excelPath = Path.Combine(filePath, $"{csvName}.xlsx");
|
|
||||||
//保存文档
|
|
||||||
workbook.SaveToFile(Path.Combine(filePath, $"{csvName}.xlsx"), ExcelVersion.Version2013);
|
|
||||||
return excelPath;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+55
-66
@@ -9,46 +9,58 @@ namespace ExcelTool
|
|||||||
{
|
{
|
||||||
public static class ExcelHelper
|
public static class ExcelHelper
|
||||||
{
|
{
|
||||||
public static List<TableExcelHeader> ExcelHeaders(string fileName, out string sheetName, out int sheetCount, int sheetNum = 0)
|
public static List<ParsedSheet> ParseAllSheets(string fileName)
|
||||||
|
{
|
||||||
|
List<ParsedSheet> result = [];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using FileStream fs = File.OpenRead(fileName);
|
||||||
|
XSSFWorkbook wk = new(fs);
|
||||||
|
int sheetCount = wk.NumberOfSheets;
|
||||||
|
|
||||||
|
for (int sheetNum = 0; sheetNum < sheetCount; sheetNum++)
|
||||||
|
{
|
||||||
|
ISheet sheet = wk.GetSheetAt(sheetNum);
|
||||||
|
string sheetName = sheet.SheetName;
|
||||||
|
|
||||||
|
// # 开头的 sheet 跳过
|
||||||
|
if (string.IsNullOrEmpty(sheetName) || sheetName.StartsWith('#'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var parsed = ParseSheet(sheet, sheetName);
|
||||||
|
if (parsed != null)
|
||||||
|
result.Add(parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ex.ToString().WriteErrorLine();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParsedSheet ParseSheet(ISheet sheet, string sheetName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
List<TableExcelHeader> headers = [];
|
IRow nameRow = sheet.GetRow(0);
|
||||||
|
IRow typeRow = sheet.GetRow(1);
|
||||||
using FileStream fs = File.OpenRead(fileName);
|
IRow descRow = sheet.GetRow(2);
|
||||||
XSSFWorkbook wk = new(fs);
|
|
||||||
|
|
||||||
sheetCount = wk.NumberOfSheets;
|
|
||||||
if (sheetNum >= sheetCount)
|
|
||||||
{
|
|
||||||
sheetName = "";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ISheet sheet = wk.GetSheetAt(sheetNum);
|
|
||||||
sheetName = sheet.SheetName;
|
|
||||||
if (sheetName.StartsWith('#'))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
IRow nameRow = sheet.GetRow(0); // 字段名
|
|
||||||
IRow typeRow = sheet.GetRow(1); // 类型
|
|
||||||
IRow descRow = sheet.GetRow(2); // 注释
|
|
||||||
|
|
||||||
|
var headers = new List<TableExcelHeader>();
|
||||||
for (int j = 0; j < nameRow.LastCellNum; j++)
|
for (int j = 0; j < nameRow.LastCellNum; j++)
|
||||||
{
|
{
|
||||||
string fieldName = nameRow.GetCell(j)?.ToString() ?? "";
|
string fieldName = nameRow.GetCell(j)?.ToString() ?? "";
|
||||||
string fieldType = typeRow.GetCell(j)?.ToString() ?? "";
|
string fieldType = typeRow.GetCell(j)?.ToString() ?? "";
|
||||||
string fieldDesc = descRow.GetCell(j)?.ToString() ?? "";
|
string fieldDesc = descRow.GetCell(j)?.ToString() ?? "";
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(fieldName))
|
if (string.IsNullOrEmpty(fieldName))
|
||||||
{
|
{
|
||||||
$"列 {j} 字段名为空".WriteErrorLine();
|
$"列 {j} 字段名为空".WriteErrorLine();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.Add(new TableExcelHeader()
|
headers.Add(new TableExcelHeader
|
||||||
{
|
{
|
||||||
FieldName = fieldName,
|
FieldName = fieldName,
|
||||||
FieldType = fieldType,
|
FieldType = fieldType,
|
||||||
@@ -56,60 +68,34 @@ namespace ExcelTool
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
// 数据从第 6 行开始(0-indexed)
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ex.ToString().WriteErrorLine();
|
|
||||||
sheetName = null;
|
|
||||||
sheetCount = 0;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TableExcelData ExcelData(string fileName, out string sheetName, out int sheetCount, int sheetNum = 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var excelHeader = ExcelHeaders(fileName, out sheetName, out sheetCount, sheetNum);
|
|
||||||
var tableRows = new List<TableExcelRow>();
|
var tableRows = new List<TableExcelRow>();
|
||||||
|
|
||||||
using FileStream fs = File.OpenRead(fileName);
|
|
||||||
IWorkbook wk = new XSSFWorkbook(fs);
|
|
||||||
|
|
||||||
if (sheetNum >= sheetCount || sheetName.StartsWith('#'))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ISheet sheet = wk.GetSheetAt(sheetNum);
|
|
||||||
|
|
||||||
for (int i = 5; i <= sheet.LastRowNum; i++)
|
for (int i = 5; i <= sheet.LastRowNum; i++)
|
||||||
{
|
{
|
||||||
IRow row = sheet.GetRow(i);
|
IRow row = sheet.GetRow(i);
|
||||||
if (row == null) continue;
|
if (row == null) continue;
|
||||||
|
|
||||||
var tableExcelRow = new TableExcelRow();
|
var tableExcelRow = new TableExcelRow();
|
||||||
|
for (int j = 0; j < headers.Count; j++)
|
||||||
for (int j = 0; j < excelHeader.Count; j++)
|
tableExcelRow.Add(GetCellValue(row.GetCell(j)));
|
||||||
{
|
|
||||||
var cell = row.GetCell(j);
|
|
||||||
tableExcelRow.Add(GetCellValue(cell));
|
|
||||||
}
|
|
||||||
|
|
||||||
tableRows.Add(tableExcelRow);
|
tableRows.Add(tableExcelRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TableExcelData(excelHeader, tableRows);
|
return new ParsedSheet
|
||||||
|
{
|
||||||
|
SheetName = sheetName,
|
||||||
|
Headers = headers,
|
||||||
|
Data = new TableExcelData(headers, tableRows),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
ex.ToString().WriteErrorLine();
|
ex.ToString().WriteErrorLine();
|
||||||
sheetName = null;
|
|
||||||
sheetCount = 0;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetCellValue(ICell cell)
|
private static string GetCellValue(ICell cell)
|
||||||
{
|
{
|
||||||
if (cell == null)
|
if (cell == null)
|
||||||
@@ -121,11 +107,7 @@ namespace ExcelTool
|
|||||||
return cell.StringCellValue;
|
return cell.StringCellValue;
|
||||||
|
|
||||||
case CellType.Numeric:
|
case CellType.Numeric:
|
||||||
if (DateUtil.IsCellDateFormatted(cell))
|
return DateUtil.IsCellDateFormatted(cell) ? cell.DateCellValue.ToString() : cell.NumericCellValue.ToString();
|
||||||
{
|
|
||||||
return cell.DateCellValue.ToString();
|
|
||||||
}
|
|
||||||
return cell.NumericCellValue.ToString();
|
|
||||||
|
|
||||||
case CellType.Boolean:
|
case CellType.Boolean:
|
||||||
return cell.BooleanCellValue ? "1" : "0";
|
return cell.BooleanCellValue ? "1" : "0";
|
||||||
@@ -138,4 +120,11 @@ namespace ExcelTool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ParsedSheet
|
||||||
|
{
|
||||||
|
public string SheetName { get; init; }
|
||||||
|
public List<TableExcelHeader> Headers { get; init; }
|
||||||
|
public TableExcelData Data { get; init; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,6 @@ namespace ExcelTool
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class FileManager
|
public static class FileManager
|
||||||
{
|
{
|
||||||
public static bool CreateDir(string dirPath)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(dirPath))
|
|
||||||
return false;
|
|
||||||
if (Directory.Exists(dirPath))
|
|
||||||
Directory.Delete(dirPath, true);
|
|
||||||
Directory.CreateDirectory(dirPath);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>将数据写入二进制文件(IBinarySerializable 版本)</summary>
|
/// <summary>将数据写入二进制文件(IBinarySerializable 版本)</summary>
|
||||||
public static bool WriteBinaryDataToFile(string filePath, IBinarySerializable data)
|
public static bool WriteBinaryDataToFile(string filePath, IBinarySerializable data)
|
||||||
{
|
{
|
||||||
@@ -67,7 +57,7 @@ namespace ExcelTool
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleHelper.WriteErrorLine($"写入二进制文件:数据类型 \"{rawType}\" 未注册,请在 TypeRegistry 中添加");
|
$"写入二进制文件:数据类型 \"{rawType}\" 未注册,请在 TypeRegistry 中添加".WriteErrorLine();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +66,7 @@ namespace ExcelTool
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
ConsoleHelper.WriteErrorLine(ex.ToString());
|
ex.ToString().WriteErrorLine();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +81,7 @@ namespace ExcelTool
|
|||||||
var elemDesc = TypeRegistry.Get(innerType);
|
var elemDesc = TypeRegistry.Get(innerType);
|
||||||
if (elemDesc == null)
|
if (elemDesc == null)
|
||||||
{
|
{
|
||||||
ConsoleHelper.WriteErrorLine($"list<T> 的元素类型 \"{innerType}\" 未注册,请在 TypeRegistry 中添加");
|
$"list<T> 的元素类型 \"{innerType}\" 未注册,请在 TypeRegistry 中添加".WriteErrorLine();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +130,7 @@ namespace ExcelTool
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static bool WriteToFile(string filePath, string context) =>
|
public static bool WriteToFile(string filePath, string context) =>
|
||||||
WriteToFile(filePath, context, Encoding.Default);
|
WriteToFile(filePath, context, new UTF8Encoding(false));
|
||||||
|
|
||||||
public static bool WriteToFile(string filePath, string context, Encoding encoding)
|
public static bool WriteToFile(string filePath, string context, Encoding encoding)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,53 +10,39 @@ namespace ExcelTool.Parser
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 生成对应的 C# Model 类
|
/// 生成对应的 C# Model 类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool GenCSharpModel(string fileName, string outputDir, string nameSpace = "")
|
public static bool GenCSharpModel(List<ParsedSheet> parsedSheets, string outputDir, string nameSpace = "")
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
"GenCSharpModel 参数传递有误".WriteErrorLine();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = new(fileName);
|
foreach(ParsedSheet sheet in parsedSheets)
|
||||||
if (string.IsNullOrEmpty(outputDir))
|
|
||||||
outputDir = fileInfo.DirectoryName;
|
|
||||||
|
|
||||||
for (int sheetNum = 0; ; sheetNum++)
|
|
||||||
{
|
{
|
||||||
var headers = ExcelHelper.ExcelHeaders(fileName, out string sheetName, out int sheetCount, sheetNum);
|
StringBuilder sb = new();
|
||||||
if (headers == null || headers.Count == 0 || sheetName.StartsWith("#") || sheetNum > sheetCount)
|
sb.Append($"/*\n * auto generated by tools(注意:千万不要手动修改本文件)\n * {sheet.SheetName}\n */\n");
|
||||||
break;
|
sb.Append("using System;\nusing System.IO;\nusing System.Collections.Generic;\nusing System.Text;\n\n");
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.Append($"/*\n * auto generated by tools(注意:千万不要手动修改本文件)\n * {sheetName}\n */\n");
|
|
||||||
sb.Append("using System;\nusing System.IO;\nusing System.Collections.Generic;\nusing System.Text;\nusing WKMobile.Generated;\n\n");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(nameSpace))
|
if (!string.IsNullOrEmpty(nameSpace))
|
||||||
sb.Append($"namespace {nameSpace}\n{{\n");
|
sb.Append($"namespace {nameSpace}\n{{\n");
|
||||||
|
|
||||||
// ── 数据行类 ─────────────────────────────────────────────────
|
// ── 数据行类 ─────────────────────────────────────────────────
|
||||||
sb.Append("[Serializable]\n");
|
sb.Append("[Serializable]\n");
|
||||||
sb.Append($"public partial class {sheetName} : IBinarySerializable\n");
|
sb.Append($"public partial class {sheet.SheetName} : IBinarySerializable\n");
|
||||||
sb.Append("{\n");
|
sb.Append("{\n");
|
||||||
|
|
||||||
foreach (var header in headers)
|
foreach (var header in sheet.Headers)
|
||||||
AppendProperty(sb, header);
|
AppendProperty(sb, header);
|
||||||
|
|
||||||
AppendDeserializeMethod(sb, headers, sheetName);
|
AppendDeserializeMethod(sb, sheet.Headers, sheet.SheetName);
|
||||||
AppendSerializeMethod(sb, headers, sheetName);
|
AppendSerializeMethod(sb, sheet.Headers, sheet.SheetName);
|
||||||
|
|
||||||
sb.Append("}\n\n");
|
sb.Append("}\n\n");
|
||||||
|
|
||||||
// ── Config 容器类 ─────────────────────────────────────────────
|
// ── Config 容器类 ─────────────────────────────────────────────
|
||||||
AppendConfigClass(sb, sheetName);
|
AppendConfigClass(sb, sheet.SheetName);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(nameSpace))
|
if (!string.IsNullOrEmpty(nameSpace))
|
||||||
sb.Append("}\n");
|
sb.Append("}\n");
|
||||||
|
|
||||||
FileManager.WriteToFile(Path.Combine(outputDir, $"{sheetName}.cs"), sb.ToString());
|
FileManager.WriteToFile(Path.Combine(outputDir, $"{sheet.SheetName}.cs"), sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -234,16 +220,9 @@ namespace ExcelTool.Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>从 TypeDescriptor 反推 reader.ReadXxx() 表达式(用于 list<T> 代码生成)</summary>
|
/// <summary>从 TypeDescriptor 反推 reader.ReadXxx() 表达式(用于 list<T> 代码生成)</summary>
|
||||||
private static string GetReadExpr(TypeDescriptor desc)
|
static string GetReadExpr(TypeDescriptor desc)
|
||||||
{
|
{
|
||||||
// GenDeserialize 生成的行形如 "\t\tname = reader.ReadXxx();\n"
|
return desc.ReadExpression ?? $"/* unknown read for {desc.TypeName} */";
|
||||||
// 这里简单地从注册表拿一个占位 name 然后截取表达式部分
|
|
||||||
const string placeholder = "__x__";
|
|
||||||
var line = desc.GenDeserialize(placeholder).Trim(); // "x = reader.ReadXxx();"
|
|
||||||
var eqIdx = line.IndexOf('=');
|
|
||||||
return eqIdx >= 0
|
|
||||||
? line[(eqIdx + 1)..].TrimEnd(';').Trim() // "reader.ReadXxx()"
|
|
||||||
: $"/* unknown read for {desc.TypeName} */";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,35 +6,21 @@ namespace ExcelTool.Parser
|
|||||||
{
|
{
|
||||||
public static class TableExcelExportBytes
|
public static class TableExcelExportBytes
|
||||||
{
|
{
|
||||||
public static bool ExportToFile(string fileName, string outputDir = null)
|
public static bool ExportToFile(List<ParsedSheet> parsedSheets, string outputDir = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = new(fileName);
|
foreach(ParsedSheet sheet in parsedSheets)
|
||||||
if (string.IsNullOrEmpty(outputDir))
|
|
||||||
{
|
{
|
||||||
outputDir = fileInfo.DirectoryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int sheetNum = 0; ; sheetNum++)
|
|
||||||
{
|
|
||||||
var tableData = ExcelHelper.ExcelData(fileName, out string sheetName, out int sheetCount, sheetNum);
|
|
||||||
if (tableData == null || sheetNum >= sheetCount)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// # 开头的 sheet 只跳过
|
|
||||||
if (sheetName.StartsWith("#"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
List<Tuple<string, string>> datas = [];
|
List<Tuple<string, string>> datas = [];
|
||||||
//先写入行数,然后每一行的数据一次写入 小写类型、字符串
|
//先写入行数,然后每一行的数据一次写入 小写类型、字符串
|
||||||
Tuple<string, string> rowCount = new("int", tableData.RowCounts.ToString());
|
Tuple<string, string> rowCount = new("int", sheet.Data.RowCounts.ToString());
|
||||||
datas.Add(rowCount);
|
datas.Add(rowCount);
|
||||||
foreach (var row in tableData.Rows)
|
foreach (var row in sheet.Data.Rows)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < tableData.CollonCount; i++)
|
for (int i = 0; i < sheet.Data.CollonCount; i++)
|
||||||
{
|
{
|
||||||
var type = tableData.Headers[i].FieldType.ToLower();
|
var type = sheet.Data.Headers[i].FieldType.ToLower();
|
||||||
var data = row.StrList[i];
|
var data = row.StrList[i];
|
||||||
|
|
||||||
// 未在 TypeRegistry 注册的类型(且不是 list<T>)当枚举处理,写入 byte
|
// 未在 TypeRegistry 注册的类型(且不是 list<T>)当枚举处理,写入 byte
|
||||||
@@ -46,7 +32,7 @@ namespace ExcelTool.Parser
|
|||||||
datas.Add(new Tuple<string, string>(type, data));
|
datas.Add(new Tuple<string, string>(type, data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var binaryFilePath = Path.Combine(outputDir, $"{sheetName}.bytes");
|
var binaryFilePath = Path.Combine(outputDir, $"{sheet.SheetName}.bytes");
|
||||||
FileManager.WriteBinaryDatasToFile(binaryFilePath, datas);
|
FileManager.WriteBinaryDatasToFile(binaryFilePath, datas);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -59,6 +45,6 @@ namespace ExcelTool.Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsGenericList(string type) =>
|
private static bool IsGenericList(string type) =>
|
||||||
type.StartsWith("list<") && type.EndsWith(">");
|
type.StartsWith("list<") && type.EndsWith('>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-35
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using ExcelTool.Parser;
|
using ExcelTool.Parser;
|
||||||
|
|
||||||
namespace ExcelTool
|
namespace ExcelTool
|
||||||
@@ -46,45 +45,32 @@ namespace ExcelTool
|
|||||||
{
|
{
|
||||||
outputDataDir = outputCodeDir;
|
outputDataDir = outputCodeDir;
|
||||||
}
|
}
|
||||||
|
// TODO 使用System.CommandLine重构参数解析
|
||||||
|
|
||||||
DirectoryInfo dirInfo = new(path);
|
DirectoryInfo dirInfo = new(path);
|
||||||
FileInfo[] csvs = dirInfo.GetFiles("*.csv", SearchOption.AllDirectories);
|
|
||||||
FileInfo[] excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
FileInfo[] excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
||||||
if ((csvs.Length <= 0) && (excels.Length <= 0))
|
if (excels.Length <= 0)
|
||||||
{
|
{
|
||||||
"当前exe目录或者目标目录没有csv文件或者excels文件,请重新设置目录".WriteErrorLine();
|
"当前exe目录或者目标目录没有excels文件,请重新设置目录".WriteErrorLine();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
"==========================================================".WriteSuccessLine();
|
"==========================================================".WriteSuccessLine();
|
||||||
"== 根据csv/xlsx生成模板代码和二进制文件工具 ==".WriteSuccessLine();
|
"== 根据xlsx生成模板代码和二进制文件工具 ==".WriteSuccessLine();
|
||||||
"== 说明:将exe放在csv/xlsx目录中或者exe或者传入csv根目录 ==".WriteSuccessLine();
|
"== 说明:将exe放在xlsx目录中或者exe或者传入根目录 ==".WriteSuccessLine();
|
||||||
"==========================================================".WriteSuccessLine();
|
"==========================================================".WriteSuccessLine();
|
||||||
|
|
||||||
List<string> genExcels = [];
|
|
||||||
foreach (FileInfo csv in csvs)
|
|
||||||
{
|
|
||||||
//生成对应的xlsx文件
|
|
||||||
var tempPath = CsvHelper.CsvToXlsx(csv.FullName);
|
|
||||||
if (string.IsNullOrEmpty(tempPath))
|
|
||||||
{
|
|
||||||
$"csv:{csv.FullName}生成xlsx文件出错".WriteErrorLine();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
genExcels.Add(tempPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
excels = dirInfo.GetFiles("*.xlsx", SearchOption.AllDirectories);
|
||||||
|
|
||||||
//读取
|
//读取
|
||||||
foreach (var file in excels)
|
foreach (FileInfo file in excels)
|
||||||
{
|
{
|
||||||
if (file.Name.StartsWith("~$")) return;
|
if (file.Name.StartsWith("~$")) continue;
|
||||||
|
|
||||||
|
List<ParsedSheet> sheets = ExcelHelper.ParseAllSheets(file.FullName);
|
||||||
|
|
||||||
//生成CS文件
|
//生成CS文件
|
||||||
bool res = GenModels.GenCSharpModel(file.FullName, outputCodeDir, nameSpace);
|
bool res = GenModels.GenCSharpModel(sheets, outputCodeDir, nameSpace);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
$"{file.Name}CS模板生成成功".WriteSuccessLine();
|
$"{file.Name}CS模板生成成功".WriteSuccessLine();
|
||||||
@@ -95,7 +81,7 @@ namespace ExcelTool
|
|||||||
}
|
}
|
||||||
|
|
||||||
//生成二进制文件,如果list或者vector数据为空则写入0,要根据类型来读取csv的字段数据强转成对应的数据类型然后写入
|
//生成二进制文件,如果list或者vector数据为空则写入0,要根据类型来读取csv的字段数据强转成对应的数据类型然后写入
|
||||||
res = TableExcelExportBytes.ExportToFile(file.FullName, outputDataDir);
|
res = TableExcelExportBytes.ExportToFile(sheets, outputDataDir);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
$"{file.Name}二进制数据生成成功".WriteSuccessLine();
|
$"{file.Name}二进制数据生成成功".WriteSuccessLine();
|
||||||
@@ -105,17 +91,10 @@ namespace ExcelTool
|
|||||||
$"{file.Name}二进制数据生成失败".WriteErrorLine();
|
$"{file.Name}二进制数据生成失败".WriteErrorLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 抽象 ExcelProcess 类 处理相关流程
|
||||||
|
|
||||||
//删除生成的excels
|
// TODO 单元测试
|
||||||
for (int i = genExcels.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
File.Delete(genExcels[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<int, string> dics = new();
|
|
||||||
new List<string>(dics.Values);
|
|
||||||
|
|
||||||
//读取测试
|
|
||||||
//IBinarySerializable newavList = new avatarguideTestConfig();
|
//IBinarySerializable newavList = new avatarguideTestConfig();
|
||||||
//var readOK = FileManager.ReadBinaryDataFromFile(Path.Combine(path, "avatarguideTest.bytes"), ref newavList);
|
//var readOK = FileManager.ReadBinaryDataFromFile(Path.Combine(path, "avatarguideTest.bytes"), ref newavList);
|
||||||
//if (readOK)
|
//if (readOK)
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace ExcelTool
|
|||||||
/// 生成 Serialize 方法体片段(变量名 → 代码行)
|
/// 生成 Serialize 方法体片段(变量名 → 代码行)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<string, string> GenSerialize { get; init; }
|
public Func<string, string> GenSerialize { get; init; }
|
||||||
|
|
||||||
|
/// <summary>用于代码生成的单次读取表达式,例如 "reader.ReadInt32()"</summary>
|
||||||
|
public string ReadExpression { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -158,6 +161,7 @@ namespace ExcelTool
|
|||||||
{
|
{
|
||||||
TypeName = typeName,
|
TypeName = typeName,
|
||||||
CSharpType = csType,
|
CSharpType = csType,
|
||||||
|
ReadExpression = readExpr,
|
||||||
WriteBinary = writeBinary,
|
WriteBinary = writeBinary,
|
||||||
GenDeserialize = name => $"\t\t{name} = {readExpr};\n",
|
GenDeserialize = name => $"\t\t{name} = {readExpr};\n",
|
||||||
GenSerialize = name => $"\t\twriter.Write({name});\n",
|
GenSerialize = name => $"\t\twriter.Write({name});\n",
|
||||||
@@ -194,7 +198,7 @@ namespace ExcelTool
|
|||||||
/// <summary>生成 List<T> 的 DeSerialize 片段</summary>
|
/// <summary>生成 List<T> 的 DeSerialize 片段</summary>
|
||||||
public static string GenListDeserialize(string name, string elemCsType, string readElemExpr)
|
public static string GenListDeserialize(string name, string elemCsType, string readElemExpr)
|
||||||
{
|
{
|
||||||
var camel = StringExtensions.ToCamelCase(name);
|
string camel = name.ToCamelCase();
|
||||||
return
|
return
|
||||||
$"\t\tvar {camel}Count = reader.ReadInt32();\n" +
|
$"\t\tvar {camel}Count = reader.ReadInt32();\n" +
|
||||||
$"\t\tif ({camel}Count > 0)\n" +
|
$"\t\tif ({camel}Count > 0)\n" +
|
||||||
@@ -231,7 +235,7 @@ namespace ExcelTool
|
|||||||
|
|
||||||
private static string GenVectorListDeserialize(string name)
|
private static string GenVectorListDeserialize(string name)
|
||||||
{
|
{
|
||||||
var camel = StringExtensions.ToCamelCase(name);
|
string camel = name.ToCamelCase();
|
||||||
return
|
return
|
||||||
$"\t\tvar {camel}Count = reader.ReadInt32();\n" +
|
$"\t\tvar {camel}Count = reader.ReadInt32();\n" +
|
||||||
$"\t\tif ({camel}Count > 0)\n" +
|
$"\t\tif ({camel}Count > 0)\n" +
|
||||||
|
|||||||
Reference in New Issue
Block a user