0. 前言
项目已经开源,源代码相关文章在见下方链接,本文为该项目的结构解释。
[编程工具]UnityGameFramework配表多语言导出工具(零)使用概述以及源码
https://blog.csdn.net/Blue_carrot_/article/details/130280639
[编程工具]UnityGameFramework配表多语言导出工具(一)Config,XML,Log
https://blog.csdn.net/Blue_carrot_/article/details/130313674
1. Program
ok,我们书接上回,今天讲讲Program,程序入口,其实主要就是两个内容,一个是加载配置,二是处理。最后做结果输出以及是否暂停等待。如下:
static void Main(string[] args)
{
// 加载配置
Config.LoadFromXss();
// 处理文件
if (!Log.ErrorFlag)
{
Exportor e = new Exportor();
e.Precess();
}
// 结束
if (Log.ErrorFlag)
{
Log.PrintLog("处理失败");
}
else
{
Log.PrintLog("处理完成");
}
// 等待
if ((Log.ErrorFlag && Config.IsWaitWhenFail) ||
(!Log.ErrorFlag && Config.IsWaitWhenSucceed))
{
Console.ReadLine();
}
}
这个其实后面可以再做输入参数处理,比如把config的配置路径处理为可配置的,比如cmd上打“exporter.exe config2.xlsx”,这样的方式来使用第二种配置,不过这个后面有需求再改吧。
然后,Config上次已经讲过了,就是配置以及加载配置的内容,而Exportor,我们先需要讲一下Property。
2. Property
这个是每个sheet中属性的基本单元,每一列都会视为是一个属性,比如map表格如下,
different | outputKey | ||
---|---|---|---|
Id | NextId | MapType | Name |
int | int | int | string |
Id | NextId | MapType | Name |
序号 | 下一个序号 | 地图类型 | 名字 |
1 | 2 | 1001 | 射击引导 |
2 | 3 | 1002 | 方块引导 |
那我们是希望能够导出为4个属性,Id NextId MapType Name,这4个属性,那么可以看出,对于每个属性,我们可以将其抽象为Property,那么他需要的属性有
public int Column;
public string Name;
public string Type;
public string Note;
public string Setting;
public List\<string\> Content;
- column—所在行,这个是为了方便后续数据载入。
- Name—属性名
- Type—属性类型
- Note—属性注释
- Content—属性数据,也就是这个属性行对应的数据。
- Setting—处理标记,暂时处理了两种,different要求数据完全不同,outputKey是指该属性需要导出为多语言key。
因为我们还需要对属性的数据做校验,格式化,或者其他标记的处理,这让我们不能够只是单纯的将数据Add到Content。比如表格中的【id】属性,标记为different,那如果之前存在该属性,则不应该导出成功,所以我们可以将数据添加处理为
public void Add(string fileName, string sheetName,int rowIndex,string content)
{
// 格式化
content = FormatContent(Type , content);
// 检查
if (isDifferent && Content.Contains(content))
{
// 判断是否重复
content = "";
Log.PrintCellError(rowIndex, Column, "当前属性设置标记为isDifferent,属性不可重复。该位置属性已重复。");
}
else if (!tryPrase(Type, content))
{
// 转化数据失败
content = "";
Log.PrintCellError(rowIndex, Column, "当前属性类型为"+ Type+ ",此数据并不是该类型,转换失败。");
}
// 加入
Content.Add(content);
}
具体格式化FormatContent以及tryPrase几个函数就不展开了,基本就是int.Parse这种,检查之类的。
3. Exportor
Exporter是处理导出文件的内容。首先我们要将文件先打开,获取文件sheet数据逐一处理。每个sheet都视为一个数据类,那么对于单独一个sheet,我们需要做的有三件事
- 加载解析数据为Property属性(getSheetData),
- 保存数据文件(saveSheetData),
- 保存代码文件(saveSheetCode),
那么processSheet这部分内容就如下:
private bool processSheet(string fileName, string sheetName, List<List<string>> sheetData)
{
bool flag = false;
List<Property> properties = getSheetData(fileName, sheetName, sheetData);
if (properties != null)
{
try
{
saveSheetData(fileName, sheetName, properties);
flag = true;
}
catch
{
Log.PrintError("保存数据文件错误");
}
try
{
saveSheetCode(fileName, sheetName, properties);
flag = true;
}
catch
{
Log.PrintError("保存代码文件错误");
}
}
return flag;
}
getSheetData
对于解析表格数据为属性,我的做法是先将数据按列处理,把所有的属性名字,类型都获取,然后再按行,将每一行对应的数据录入。具体如下:
private List<Property> getSheetData(string fileName, string sheetName, List<List<string>> sheetData)
{
List<Property> properties = new List<Property>();
List<string> firstList = GetRowData(sheetData,0);
List<string> settingList = GetRowData(sheetData, Config.PropertySettingRow);
List<string> nameList = GetRowData(sheetData, Config.PropertyNameRow);
List<string> typeList = GetRowData(sheetData, Config.PropertyTypeRow);
List<string> noteList = GetRowData(sheetData, Config.PropertyNoteRow);
HashSet<string> nameHashSet = new HashSet<string>();
//读取属性
for (int i = 0; i < nameList.Count; i++)
{
string first = GetCellString(firstList, i);
string setting = GetCellString(settingList, i);
string name = GetCellString(nameList, i);
string type = GetCellString(typeList, i);
string note = GetCellString(noteList, i);
if (name == "")
{
}
else if (Config.IsIgnoreColumnWhichFirstRowIsHashTag && first == "#")
{
}
else if (!nameHashSet.Add(name))
{
Log.PrintCellError(Config.PropertyNameRow, i, "属性重复," + name);
}
else if (Array.IndexOf(matchType, type) < 0)
{
//Console.WriteLine("matchType:" + matchType.Length);
//Console.WriteLine("type:" + type);
//属性错误
Log.PrintCellError(Config.PropertyTypeRow, i, "属性类型错误," + type);
}
else
{
Property property = new Property(i, name, type, note, setting);
properties.Add(property);
}
}
//读取属性值
for (int r = Config.PropertyStartRow; r < sheetData.Count; r++)
{
List<string> row = sheetData[r];
string firstCell = GetCellString(row, 0);
string firstValue = properties.Count > 0 ? GetCellString(row, properties[0].Column) : "";
if (Config.IsIgnoreColumnWhichFirstRowIsHashTag && firstCell == "#") { }
else if (Config.IsIgnoreRowWhichFirstPropertyIsEmpty && firstValue == "") { }
else
{
foreach (Property item in properties)
{
item.Add(fileName, sheetName, r, GetCellString(row, item.Column));
}
}
}
return properties;
}
private List<string> GetRowData(List<List<string>> sheetData, int row)
{
return row >= sheetData.Count ? null : sheetData[row];
}
private string GetCellString(List<string> rowData, int column)
{
return rowData == null || column >= rowData.Count ? "" : rowData[column];
}
saveSheetData
这部分内容,就是将properties中的数据依次加上制表符导出到文件中就可以了,因为UGF中也是按制表符划分的。这里如果有多语言导出配置,其实要保存到数据是key,而内容content也需要做一个额外记录,以便在最后导出多语言文件。
private void saveSheetData(string fileName, string sheetName, List<Property> properties)
{
string savePath = Config.SaveDataPath + "\\" + Config.GetDataFileName(fileName, sheetName);
// Log.PrintError(savePath);
List<StringBuilder> strList = new List<StringBuilder>();
int cCount = properties.Count;
for (int c = 0; c < cCount; c++)
{
if (properties[c].IsOutputKey)
{
OutputLog.Add(Config.GetOutputKeyNode(fileName, sheetName, properties[c]).ToString());
}
List<string> content = properties[c].Content;
int rCount = content.Count;
for (int r = 0; r < rCount; r++)
{
while (strList.Count <= r)
{
strList.Add(new StringBuilder());
}
if (properties[c].IsOutputKey && content[r] != "")
{
//id:properties[0].Content[r]
//value:properties[0].Content[r]
string key = Config.GetOutputKeyText(fileName, sheetName, properties[c], properties[0].Content[r]).ToString();
OutputLog.Add(Config.GetOutputKeyItem(fileName, sheetName, key, content[r], properties[c]).ToString());
strList[r].Append("\t" + key);
}
else {
strList[r].Append("\t" + content[r]);
}
}
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < strList.Count; i++)
{
result.Append(strList[i].Remove(0, 1));
result.Append('\n');
}
if (result.Length>=1) {
result.Remove(result.Length - 1, 1);
}
File.WriteAllText(savePath, result.ToString());
}
saveSheetCode
保存代码这部分内容为了让代码内容方便更改,使用的是模板替换的方式来处理的,这样如果后面只是想改改缩进,或者函数名,基础之类的,只要改模板就可以了。这个部分,假如模板是
那么红色的部分都回替换为对应的内容,而蓝色的标记,主要是为了重复属性段而做的。具体的导出代码就不放了比较长。
到这里就已经将sheet都已经处理完了。Exporter的内容就是逐一处理表格(导出数据和代码),然后再统一导出多语言xml,然后就结束了。
4. 结束
到这里已经将项目都讲完了,不过项目还有很多可以改进的点,或者新增的内容。比如
- 增加字典类型数据,只有数组有些时候还是不是很方便
- 处理多文件同名sheet导出为同个文件,方便多人版本协同
- 增加关联属性,如果能够关联到别的表,或者别的数据类型会更好,不过这个感觉不是很容易改。