查看: 1845|回复: 4
打印 上一主题 下一主题

[经验分享] CSV解析工具

[复制链接]

100

主题

3

听众

7683

积分

高级设计师

Rank: 6Rank: 6

纳金币
2378
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2015-2-5 22:05:33 |只看该作者 |倒序浏览
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。那么自行处理即可~
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

ZackD    

715

主题

22

听众

4万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
26224
精华
17

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

沙发
发表于 2015-2-5 22:10:55 |只看该作者
代码可以不要用斜体吗。。
回复

使用道具 举报

115

主题

3

听众

5676

积分

高级设计师

Rank: 6Rank: 6

纳金币
7268
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

板凳
发表于 2015-2-5 22:23:04 |只看该作者
Thanks for sharing this one !
回复

使用道具 举报

3

主题

1

听众

6189

积分

高级设计师

Rank: 6Rank: 6

纳金币
370
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

地板
发表于 2015-2-5 23:58:18 |只看该作者
代码可以不要用斜体吗
回复

使用道具 举报

10

主题

0

听众

551

积分

初级设计师

Rank: 3Rank: 3

纳金币
586
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

5#
发表于 2015-2-8 13:19:18 |只看该作者
感谢分享,谢谢提供下载
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-11-15 10:36 , Processed in 0.133097 second(s), 26 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部