Unity3D 异步读取CSV文件
误打误撞在VR圈混迹多年,终于下决心要回来做MMOARPG手游了。
很久没有这么“充实”过了,项目进度又到了读表阶段了。
看到前些年使用的CSV读取工具类还有优化空间,所以修改后发出来跟大家交流交流,还望各路大神多加指点。
注释都在代码里,so,废话不多说直接上代码:
<font color="#000"><font face="Arial">using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Text;
/// <summary>
/// 读取CSV工具类
/// (需求:UTF-8格式)
/// </summary>
public class LoadCSVData : MonoBehaviour
{
public delegate void toDirectoryCallBack(Dictionary<string, List<string>> csvdata);
/// <summary>
/// 异步开始读取CSV文档
/// </summary>
/// <param name="Filename">StreamingAssets目录内的文件名</param>
/// <param name="LoadCBK">读取完成后的回调事件</param>
/// <returns></returns>
public IEnumerator LoadCSV_toDirectory(string Filename,toDirectoryCallBack LoadCBK)
{
string CSVText = null;
yield return StartCoroutine(LoadWWW(GetFilepath(Filename), (www) => { CSVText = www; }));
Dictionary<string, List<string>> CSVData = getDirectory(readcsv(CSVText.ToString()));
LoadCBK(CSVData);
}
/// <summary>
/// 获取完整的文件路径
/// </summary>
/// <param name="Filename"></param>
/// <returns></returns>
string GetFilepath(string Filename)
{
switch (Application.platform)
{
case RuntimePlatform.Android:
return "jar:file://" + Application.dataPath + "!/assets/" + Filename + ".csv";
case RuntimePlatform.IPhonePlayer:
return Application.dataPath + "/Raw/" + Filename + ".csv";
default:
return "file://" + Application.dataPath + "/StreamingAssets/" + Filename + ".csv";
}
}
/// <summary>
/// 读取文件
/// </summary>
/// <param name="filepath">文件路径</param>
/// <param name="loadCBK">读取完成回调</param>
/// <returns></returns>
IEnumerator LoadWWW(string filepath, Action<string> loadCBK)
{
WWW www = new WWW(filepath);
yield return www;
string CSVText;
if (!string.IsNullOrEmpty(www.error))
{
CSVText = www.error;
}
else
{
CSVText = www.text;
}
loadCBK(CSVText);
}
public static List<List<string>> readcsv(string strin)
{
return readcsv(strin, Encoding.UTF8);
}
public static List<List<string>> readcsv(string strin, Encoding encoding)
{
List<List<string>> ret = new List<List<string>>();
strin = strin.Replace("r", "");
string[] lines = strin.Split('n');
if (lines.Length > 0)
{
byte[] byt = encoding.GetBytes(lines[0]);
if (byt.Length >= 3 &&
byt[0] == 0xEF &&
byt[1] == 0xBB &&
byt[2] == 0xBF)
{
lines[0] = encoding.GetString(byt, 3, byt.Length - 3);
}
}
for (int i = 0; i < lines.Length; i++)
{
if (string.IsNullOrEmpty(lines[i]) ||
lines[i].StartsWith("#"))
continue;
List<string> s = split(lines[i], encoding);
ret.Add(s);
}
return ret;
}
static List<string> split(string line, Encoding encoding)
{
byte[] b = encoding.GetBytes(line);
List<string> bls = new List<string>();
int end = b.Length - 1;
List<byte> bl = new List<byte>();
bool inQuote = false;
for (int i = 0; i < b.Length; i++)
{
switch ((char)b[i])
{
case ',':
if (inQuote)
bl.Add(b[i]);
else
{
bls.Add(makefield(ref bl, encoding));
bl.Clear();
}
break;
case '"':
inQuote = !inQuote;
bl.Add((byte)'"');
break;
case '\':
if (i < end)
{
switch ((char)b[i + 1])
{
case 'n':
bl.Add((byte)'n');
i++;
break;
case 't':
bl.Add((byte)'t');
i++;
break;
case 'r':
i++;
break;
default:
bl.Add((byte)'\');
break;
}
}
else
bl.Add((byte)'\');
break;
default:
bl.Add(b[i]);
break;
}
}
bls.Add(makefield(ref bl, encoding));
bl.Clear();
return bls;
}
static string makefield(ref List<byte> bl, Encoding encoding)
{
if (bl.Count > 1 && bl[0] == '"' && bl[bl.Count - 1] == '"')
{
bl.RemoveAt(0);
bl.RemoveAt(bl.Count - 1);
}
int n = 0;
while (true)
{
if (n >= bl.Count)
break;
if (bl[n] == '"')
{
if (n < bl.Count - 1 && bl[n + 1] == '"')
{
bl.RemoveAt(n + 1);
n++;
}
else
bl.RemoveAt(n);
}
else
n++;
}
return encoding.GetString(bl.ToArray());
}
/// <summary>
/// 转换成Dictionary类型
/// </summary>
/// <param name="listin"></param>
/// <returns></returns>
public static Dictionary<string, List<string>> getDirectory(List<List<string>> listin)
{
Dictionary<string, List<string>> dir = new Dictionary<string, List<string>>();
for (int i = 0; i < listin.Count; i++)
{
if (string.IsNullOrEmpty(listin[i][0]))
continue;
dir[listin[i][0]] = listin[i];
}
return dir;
}
/// <summary>
/// 打印 Csv 转换的二维数组
/// </summary>
/// <param name="grid"></param>
public static void DebugOutputGrid(string _titleName, Dictionary<string, List<string>> grid)
{
StringBuilder textOutput = new StringBuilder();
textOutput.Append(_titleName);
textOutput.Append("n");
foreach (var line in grid)
{
for (int i = 0; i < line.Value.Count; i++)
{
string row = line.Value[i];
textOutput.Append(row);
if (i < line.Value.Count - 1)
textOutput.Append("|");
}
textOutput.Append("n");
}
Debug.Log(textOutput);
}
} </font></font><font color="#000"><font face="Arial">using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Text;
/// <summary>
/// 读取CSV工具类
/// (需求:UTF-8格式)
/// </summary>
public class LoadCSVData : MonoBehaviour
{
public delegate void toDirectoryCallBack(Dictionary<string, List<string>> csvdata);
/// <summary>
/// 异步开始读取CSV文档
/// </summary>
/// <param name="Filename">StreamingAssets目录内的文件名</param>
/// <param name="LoadCBK">读取完成后的回调事件</param>
/// <returns></returns>
public IEnumerator LoadCSV_toDirectory(string Filename,toDirectoryCallBack LoadCBK)
{
string CSVText = null;
yield return StartCoroutine(LoadWWW(GetFilepath(Filename), (www) => { CSVText = www; }));
Dictionary<string, List<string>> CSVData = getDirectory(readcsv(CSVText.ToString()));
LoadCBK(CSVData);
}
/// <summary>
/// 获取完整的文件路径
/// </summary>
/// <param name="Filename"></param>
/// <returns></returns>
string GetFilepath(string Filename)
{
switch (Application.platform)
{
case RuntimePlatform.Android:
return "jar:file://" + Application.dataPath + "!/assets/" + Filename + ".csv";
case RuntimePlatform.IPhonePlayer:
return Application.dataPath + "/Raw/" + Filename + ".csv";
default:
return "file://" + Application.dataPath + "/StreamingAssets/" + Filename + ".csv";
}
}
/// <summary>
/// 读取文件
/// </summary>
/// <param name="filepath">文件路径</param>
/// <param name="loadCBK">读取完成回调</param>
/// <returns></returns>
IEnumerator LoadWWW(string filepath, Action<string> loadCBK)
{
WWW www = new WWW(filepath);
yield return www;
string CSVText;
if (!string.IsNullOrEmpty(www.error))
{
CSVText = www.error;
}
else
{
CSVText = www.text;
}
loadCBK(CSVText);
}
public static List<List<string>> readcsv(string strin)
{
return readcsv(strin, Encoding.UTF8);
}
public static List<List<string>> readcsv(string strin, Encoding encoding)
{
List<List<string>> ret = new List<List<string>>();
strin = strin.Replace("r", "");
string[] lines = strin.Split('n');
if (lines.Length > 0)
{
byte[] byt = encoding.GetBytes(lines[0]);
if (byt.Length >= 3 &&
byt[0] == 0xEF &&
byt[1] == 0xBB &&
byt[2] == 0xBF)
{
lines[0] = encoding.GetString(byt, 3, byt.Length - 3);
}
}
for (int i = 0; i < lines.Length; i++)
{
if (string.IsNullOrEmpty(lines[i]) ||
lines[i].StartsWith("#"))
continue;
List<string> s = split(lines[i], encoding);
ret.Add(s);
}
return ret;
}
static List<string> split(string line, Encoding encoding)
{
byte[] b = encoding.GetBytes(line);
List<string> bls = new List<string>();
int end = b.Length - 1;
List<byte> bl = new List<byte>();
bool inQuote = false;
for (int i = 0; i < b.Length; i++)
{
switch ((char)b[i])
{
case ',':
if (inQuote)
bl.Add(b[i]);
else
{
bls.Add(makefield(ref bl, encoding));
bl.Clear();
}
break;
case '"':
inQuote = !inQuote;
bl.Add((byte)'"');
break;
case '\':
if (i < end)
{
switch ((char)b[i + 1])
{
case 'n':
bl.Add((byte)'n');
i++;
break;
case 't':
bl.Add((byte)'t');
i++;
break;
case 'r':
i++;
break;
default:
bl.Add((byte)'\');
break;
}
}
else
bl.Add((byte)'\');
break;
default:
bl.Add(b[i]);
break;
}
}
bls.Add(makefield(ref bl, encoding));
bl.Clear();
return bls;
}
static string makefield(ref List<byte> bl, Encoding encoding)
{
if (bl.Count > 1 && bl[0] == '"' && bl[bl.Count - 1] == '"')
{
bl.RemoveAt(0);
bl.RemoveAt(bl.Count - 1);
}
int n = 0;
while (true)
{
if (n >= bl.Count)
break;
if (bl[n] == '"')
{
if (n < bl.Count - 1 && bl[n + 1] == '"')
{
bl.RemoveAt(n + 1);
n++;
}
else
bl.RemoveAt(n);
}
else
n++;
}
return encoding.GetString(bl.ToArray());
}
/// <summary>
/// 转换成Dictionary类型
/// </summary>
/// <param name="listin"></param>
/// <returns></returns>
public static Dictionary<string, List<string>> getDirectory(List<List<string>> listin)
{
Dictionary<string, List<string>> dir = new Dictionary<string, List<string>>();
for (int i = 0; i < listin.Count; i++)
{
if (string.IsNullOrEmpty(listin[i][0]))
continue;
dir[listin[i][0]] = listin[i];
}
return dir;
}
/// <summary>
/// 打印 Csv 转换的二维数组
/// </summary>
/// <param name="grid"></param>
public static void DebugOutputGrid(string _titleName, Dictionary<string, List<string>> grid)
{
StringBuilder textOutput = new StringBuilder();
textOutput.Append(_titleName);
textOutput.Append("n");
foreach (var line in grid)
{
for (int i = 0; i < line.Value.Count; i++)
{
string row = line.Value[i];
textOutput.Append(row);
if (i < line.Value.Count - 1)
textOutput.Append("|");
}
textOutput.Append("n");
}
Debug.Log(textOutput);
}
} </font></font>
下面是使用方法:
<font color="#000"><font face="Arial">using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
public class LoadCSVDara_debug : MonoBehaviour {
// Use this for initialization
void Start ()
{
StartCoroutine(loading());
}
IEnumerator loading()
{
yield return StartCoroutine(LoadCSVData_debug());
}
/// <summary>
/// 读取CSV格式的文档
/// </summary>
/// <returns></returns>
IEnumerator LoadCSVData_debug()
{
DateTime startTime = DateTime.Now;
Debug.Log("开始读取数据");
LoadCSVData loadcsv = gameObject.AddComponent<LoadCSVData>();
Dictionary<string, List<string>> data = null;
//读取技能表
yield return StartCoroutine(loadcsv.LoadCSV_toDirectory("SKILL", (csvdata) => { data = csvdata; }));
Debug.Log("读取技能表花费:" + (DateTime.Now - startTime).TotalMilliseconds + "ms");
//SkillInfo.loadSkillDatas(data);
LoadCSVData.DebugOutputGrid("技能表", data);
}
} </font></font>
嗯,就是这样,我忙去了~~汪汪~!