在开发项目时,我们经常会用到解析配置表,读取的文件类型有csv/xls/xlsx/txt/json,无论是哪一种,都差不太多,最后都会出现字符串操作,当大量操作字符串时,string类型会额外消耗大量内存,会极大影响性能,所以使用StringBuilder类来操作字符串会更加高效。
使用StringBuilder类需要引入命名空间
using System.Text;
这是一个半成品的解析文件脚本,我的xlsx第一行是标注因此不读取
using Excel;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using UnityEngine;
public class ObjectsInfo : MonoBehaviour
{
public static ObjectsInfo instance;
//文本路径 StreamingAssets文件夹下
private string excelname = "/Object.xlsx";
//字典 id 物品
private Dictionary ObejectDic = new Dictionary();
private void Awake()
{
instance = this;
ParseText();
}
//文本解析
private void ParseText()
{
//打开文本 读
FileStream stream = File.Open(Application.streamingAssetsPath + excelname, FileMode.Open, FileAccess.Read, FileShare.Read);
//读取文件流
IExcelDataReader excelRead = ExcelReaderFactory.CreateOpenXmlReader(stream);
//转化为数据
DataSet result = excelRead.AsDataSet();
//获取表格行数
int row = result.Tables[0].Rows.Count;
//获取表格列数
int col = result.Tables[0].Columns.Count;
//数组存储
StringBuilder[] infosArray = new StringBuilder[row - 1];
Debug.Log(result.Tables[0].Rows[1][0].ToString());
//每一行
for (int i=0; i< row-1; i++)
{
infosArray[i] = new StringBuilder();
for (int j=0; j< col;j++)
{
infosArray[i].Append(result.Tables[0].Rows[i+1][j]);
if(j!=col-1)
{
infosArray[i].Append('|');
}
}
Debug.Log(infosArray[i]);
}
//每行
for (int i = 0; i < row-1; i++)
{
//物体
ObjectInfo info = new ObjectInfo();
string[] temp=infosArray[i].ToString().Split('|');
info.id = int.Parse(temp[0]);
info.name = temp[1];
info.type = (ObjectType)System.Enum.Parse(typeof(ObjectType), temp[2]);
info.uipath = temp[3];
info.des = temp[4];
ObejectDic.Add(info.id,info);
}
//关闭文件
if (stream != null)
{
stream.Close();
}
}
//根据id获取物品
public ObjectInfo GetObjectInfoById(int id)
{
ObjectInfo info = null;
ObejectDic.TryGetValue(id, out info);
return info;
}
}
public enum ObjectType
{
//武器
GUN=0
}
public class ObjectInfo
{
//id
public int id;
//物品名字
public string name;
//类型
public ObjectType type;
//UI图片路径
public string uipath;
//描述
public string des;
}
有人会说为什么要把读出的数据先用字符串拼接存起来?然后后面又拆分,为什么不直接将读出的数据转化为相应类型存进去?
首先如果直接转与存是可以实现的,但是你想想,每一行的每一列都需要进行转自符号串加上转类型操作,数据量大的时候.ToString()方法会对性能产生影响,所以先用StringBuilder拼接再分开节约内存,提升性能。
在C#中,调用.ToString()方法本身并不会显著地消耗性能。ToString()方法是一个常见的方法,用于将对象转换为字符串表示形式。
然而,需要注意的是,如果在大量的循环或频繁的操作中频繁地调用.ToString()方法,可能会对性能产生一些影响。这是因为每次调用.ToString()方法都会创建一个新的字符串对象,并且在内存中分配新的空间来存储转换后的字符串。
如果在性能敏感的代码中需要频繁地将对象转换为字符串,可以考虑使用其他更高效的方法,例如使用StringBuilder类来构建字符串,或者使用格式化字符串的方法,如string.Format()或插值字符串。
总之,对于一般的使用情况,调用.ToString()方法不会带来明显的性能问题。但在特定的性能要求下,可以考虑使用更高效的字符串操作方法来优化代码。
在C#中,可以使用StringBuilder的ToString()方法将StringBuilder的内容转化为string类型。ToString()方法会返回StringBuilder对象中存储的字符串。
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Hello");
stringBuilder.Append(" ");
stringBuilder.Append("World");
string result = stringBuilder.ToString();
Console.WriteLine(result); // 输出:Hello World
在上述示例中,首先创建了一个StringBuilder对象,并使用Append()方法向其添加了多个字符串。然后,通过调用ToString()方法将StringBuilder的内容转化为string类型,并将结果赋值给result变量。最后,通过Console.WriteLine()方法输出结果。
需要注意的是,一旦调用ToString()方法,StringBuilder对象的内容将被转化为string类型,并且StringBuilder对象将被重置为空。因此,在需要多次使用StringBuilder对象时,应该在每次使用之前重新构建它。
这样看挺费时间的,所以我们看到核心代码
//转化为数据
DataSet result = excelRead.AsDataSet();
//获取表格行数
int row = result.Tables[0].Rows.Count;
//获取表格列数
int col = result.Tables[0].Columns.Count;
//数组存储
StringBuilder[] infosArray = new StringBuilder[row - 1];
Debug.Log(result.Tables[0].Rows[1][0].ToString());
//每一行
for (int i=0; i< row-1; i++)
{
infosArray[i] = new StringBuilder();
for (int j=0; j< col;j++)
{
infosArray[i].Append(result.Tables[0].Rows[i+1][j]);
if(j!=col-1)
{
infosArray[i].Append('|');
}
}
Debug.Log(infosArray[i]);
}
//每行
for (int i = 0; i < row-1; i++)
{
//物体
ObjectInfo info = new ObjectInfo();
string[] temp=infosArray[i].ToString().Split('|');
info.id = int.Parse(temp[0]);
info.name = temp[1];
info.type = (ObjectType)System.Enum.Parse(typeof(ObjectType), temp[2]);
info.uipath = temp[3];
info.des = temp[4];
ObejectDic.Add(info.id,info);
}
其次无论你优化得多好,读取总需要时间,因此提前读,一般我们可以在切到此场景前的一个场景加载这个,比如:我切换场景时不销毁某个物体,加载一个过渡场景,停留一定时间,并为其添加解析的脚本,然后在切换到目标场景,那么我们在下一个场景依旧能得到数据,并且游戏也不会感受到卡顿。