- 最后登录
- 2020-3-16
- 注册时间
- 2014-4-17
- 阅读权限
- 90
- 积分
- 7683
- 纳金币
- 2378
- 精华
- 0
|
1、干嘛的
每个项目都会用各种各样的数据格式来保存策划的配置文件。我们也不例外。我们采用的是CSV(逗号分隔文件)。因为我们的配置文件是策划录入在数据库中的。可以可以由工具直接导出CSV格式。
那么我们就需要有个CSV的解析器,可以把CSV文件解析成对象列表(可以理解为每一行数据就是一个对象。每一列就是对象中的属性)。
2、例子
比如这样一个文件,就是CSV文件中的内容了。
注:我们这个文件是采用Navicat 11.0.7导出的CSV。本文的解析代码也是针对该版本。(有一些版本导出的时候列于列之间的内容没有用分号包围起来。到时候大家自己修改代码即可。)
3、代码
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
/* ==============================================================================
* 功能描述:CSV(逗号分割文件)解析器
* ==============================================================================*/
public class CsvParser{
public delegate T Parser<T>(List<string> lineFields);
/// <summary>
/// 转换
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="colNames"></param>
/// <param name="fieldValues"></param>
/// <param name="type"></param>
/// <returns></returns>
public static T ReflectionParser<T>(string[] colNames, string[] fieldValues, Type type){
//使用指定类型的默认构造函数来创建该类型的实例
T t = (T)System.Activator.CreateInstance(type);
for(int i=0; i<colNames.Length; i++){
//#if UNITY_EDITOR
try{
//#endif
if (colNames == null)
{
continue;
}
PropertyInfo property = t.GetType().GetProperty(colNames);
if (string.IsNullOrEmpty(fieldValues))
{
Type propertyType = property.PropertyType;
if (propertyType == typeof(string))
{
property.SetValue(t, "", null);
}
else if (propertyType == typeof(int) || propertyType == typeof(short) ||
propertyType == typeof(byte) || propertyType == typeof(float) ||
propertyType == typeof(double))
{
property.SetValue(t, 0, null);
}
else
{
UnityEngine.Debug.Log("Can not found default value for " + propertyType);
}
}
else
{
property.SetValue(t, Convert.ChangeType(fieldValues, property.PropertyType), null);
}
//#if UNITY_EDITOR
}catch(Exception e){
Debuger.Log("Error at parse for `" + typeof(T) + "` column index=" + i + ", column name=" + colNames + ", value=" + fieldValues + " value in #0 column=" + fieldValues[0] + ", error=" + e);
}
//#endif
}
return t;
}
/// <summary>
/// Csv内容的分隔符
/// </summary>
public const char DEFAULD_FIELD_SEPARATOR = ',';
/// <summary>
/// Csv内容的边界两端
/// </summary>
public const char DEFAULD_FIELD_BOUND = '"';
public static List<T> DoParse<T>(string path, Parser<T> parser){
return DoParseByReader(File.OpenText(path), parser);
}
public static List<T> DoParseByFilePath<T>(char fieldSeparator, char fieldBound, string path, Parser<T> parser){
return DoParseByReader(fieldSeparator, fieldBound, File.OpenText(path), parser);
}
public static List<T> DoParseByReader<T>(StreamReader reader, Parser<T> parser){
return DoParseByReader(DEFAULD_FIELD_SEPARATOR, DEFAULD_FIELD_BOUND, reader, parser);
}
public static List<T> DoParseByReader<T>(Type type, string[] colNames, StreamReader reader){
return DoParse<T>(type, colNames, DEFAULD_FIELD_SEPARATOR, DEFAULD_FIELD_BOUND, reader);
}
public static List<T> DoParseByFilePath<T>(Type type, string[] colNames, string path){
return DoParseByReader<T>(type, colNames, File.OpenText(path));
}
#region 对文件的内容 进行Csv格式的转换 +static List<T> DoParseByFileContent<T>(Type type, string[] colNames, string fileContent)
/// <summary>
/// 对文件的内容 进行Csv格式的转换
/// </summary>
/// <typeparam name="T">转换后所得到的类型</typeparam>
/// <param name="type">要转换的类型</param>
/// <param name="colNames">要处理的名称</param>
/// <param name="fileContent">文件的内容</param>
/// <returns>得到 进行Csv转换后的List</returns>
public static List<T> DoParseByFileContent<T>(Type type, string[] colNames, string fileContent)
{
return DoParseByFileContent<T>(DEFAULD_FIELD_SEPARATOR, DEFAULD_FIELD_BOUND, fileContent, delegate(List<string> lineFields)
{
return ReflectionParser<T>(colNames, lineFields.ToArray(), type);
});
}
#endregion
public static List<T> DoParse<T>(Type type, string[] colNames, char fieldSeparator, char fieldBound, StreamReader reader){
return DoParseByReader(fieldSeparator, fieldBound, reader, delegate(List<string> lineFields){
return ReflectionParser<T>(colNames, lineFields.ToArray(), type);
});
}
#region 对文件的内容的每一行 进行Csv格式的转换 +static List<T> DoParseByFileContent<T>(char fieldSeparator, char fieldBound, string fileContent, Parser<T> parser)
/// <summary>
/// 对文件的内容的每一行 进行Csv格式的转换
/// </summary>
/// <typeparam name="T">转换后所得到的类型</typeparam>
/// <param name="fieldSeparator">转换的分隔符</param>
/// <param name="fieldBound">转换边界符</param>
/// <param name="fileContent">文件内容</param>
/// <param name="parser">转换对象</param>
/// <returns>返回一个格式转换后得到的一个集合</returns>
public static List<T> DoParseByFileContent<T>(char fieldSeparator, char fieldBound, string fileContent, Parser<T> parser)
{
//回车换行分隔
string[] lines = fileContent.Split(new char[] { '\n' }, StringSplitOptions.None);
List<T> list = new List<T>();
//如果存在行 则遍历行 并且添加到行集合中
if (lines != null)
{
foreach (string line in lines)
{
if (line != null && !"".Equals(line.Trim()))
{
list.Add(processLine(fieldSeparator, fieldBound, line, parser));
}
}
}
return list;
}
#endregion
public static List<T> DoParseByReader<T>(char fieldSeparator, char fieldBound, StreamReader reader, Parser<T> parser){
string line = null;
List<T> list = new List<T>();
while((line=reader.ReadLine())!=null){
list.Add(processLine(fieldSeparator, fieldBound, line, parser));
}
return list;
}
private static T processLine<T>(char fieldSeparator, char fieldBound, string line, Parser<T> parser){
int index = 0;
List<string> lineFields = new List<string>();
int lastQuote = -1;
char[] cs = line.ToCharArray();
bool find = false;
for(index=0; index<cs.Length; index++){
char c = cs[index];
if(c == fieldBound){
if(lastQuote == -1){
lastQuote = index;
} else{
if(index + 1 < cs.Length && cs[index + 1] == c){
index++;
} else{
find = true;
}
}
} else if(c == fieldSeparator){
if(lastQuote != -1 && find){
lineFields.Add(line.Substring(lastQuote + 1, index - lastQuote - 2).Replace("\"\"", "\""));
find = false;
lastQuote = -1;
}
}
}
//UnityEngine.Debug.Log("..." + line);
lineFields.Add(line.Substring(lastQuote + 1, index - lastQuote - 3).Replace("\"\"", "\""));
return parser(lineFields);
}
}
代码没什么好说的,就是逐行解析。根据CSV的特征解析出每一列。
4、怎么调用
比如我有个ConfigErrorCode.txt的文件,用来保存错误码。
那么定义一个ConfigErrorCode的类(类名不用和文件名一样,你可以随便取)
/* ==============================================================================
* 功能描述:错误码
* ==============================================================================*/
public class ConfigErrorCode{
/// <summary>
/// 错误的id
/// </summary>
public int id{get; set;}
/// <summary>
/// 错误的提示信息
/// </summary>
public string text{get; set;}
}
解析代码:
string content = (Resources.Load("ConfigErrorCode") as TextAsset).text;
List<ConfigErrorCode> configError = CsvParser.DoParseByFileContent<ConfigErrorCode>(typeof(ConfigErrorCode), new string[] {
"id",
"text"
}, content);
没错,这样就可以获得一个错误码的列表了~如果你想要获得的是Dictionary。那么自行处理即可~
|
|