昨天和朋友聊天时,他遇到这么一个问题:现在有按照一定格式的数据,例如:
#code==text 此处是注释
100==确定

101==取消
key==value 这么个格式的,说白了就是怎样解析这些固定格式字符串的Key和Value而已。他们项目已经做过了数据的解析,现在他在做项目优化,发现这一块数据解析部分GC偏高,何谓GC,就是Garbage Collection,在.Net中GC是由系统自动调用的,对内存的释放和回收程序员是特别害怕遇上GC的,数据结构设计不合理,导致系统频繁调用GC,从而导致GC的代数增加,最终结果就是你的程序就会越来越卡,直到无响应!

他就问我:“要是我,我会选择什么样的方式去解析这些数据?"

我当时不假思索:“格式固定,那就按这个固定的格式去解析,不就可以了!"

方法一:我们可以按照”==“进行分割,分割后刚好得到我们想要的Key和Value!刚好系统提供了N多分割的方法,在这里刚好用字符串的分割

string[] keyPair = Regex.Split(data,@"==",RegexOptions.IgnoreCase);

/// <summary>
/// 正则表达式分割字符串
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="infoDic">解析后的kv</param>
public static void ParseDataTable(string filePath,ref Dictionary<string, string> infoDic)
{
infoDic.Clear();
if (File.Exists(filePath))
{
using (FileStream fs = new FileStream(filePath,FileMode.Open,FileAccess.Read))
{
StreamReader sr = new StreamReader(fs);
string data = sr.ReadLine();
while (data != null)
{
if (data.StartsWith("#"))//忽略注释行
{
data = sr.ReadLine();
continue;
}
else
{
//分割key和value
string[] keyPair = Regex.Split(data,@"==",RegexOptions.IgnoreCase);
if (keyPair.Length > )
{
if (!infoDic.ContainsKey(keyPair[]))
{
infoDic.Add(keyPair[], keyPair[]);
}
else
{
Debug.LogError(string.Format("[ERROR]:Has same key:{0},value:{1}",keyPair[],keyPair[]));
}
}
}
data = sr.ReadLine();
}
sr.Close();
fs.Close();
}
}
}

运行结果:


好了,方法一到此完美分割出key和value!But......

朋友说这就是他们正在使用的解析方式,正因为正则表达式使用及其的方便不需要关心它是怎么实现的所有产生大量的GC,导致我们束手无策,因为字符串匹配解析的同时会生成许多字符串临时变量,这些都要在内存堆上申请空间,在profiler中看到解析时有大概10M的GC。所以我想到的这个解析方式被否了!朋友让我继续想想有没有什么好的办法,过了一会他给我说了他的想法,为何我们不自己去写一种解析方式呢,正则耗内存,我们可以不用它,string临时变量占用内存我们也可以不用它改用StringBuilder来替代它。这么说是可行的啊!无论是我们自己解析还是使用正则去解析,这个读取还是肯定要做的,读取后针对这个string我们逐字节去解析,特殊字符就去特殊处理,添加特殊的标记,例如:
#’:表示该行是注释行,解析时可以忽略;
\n’:表示要换行了,也意味着接下来key要出现了;
\r’:回车键的标识符号;
=’:这是一个很重要的符号,这个要特殊照顾,对它采取计数,奇数个出现时刚好是key的结束位置,偶数个出现时刚好是value的起始位置,这里是不是信息量很大,你会很快的  想到计数个数对2取余做判断处理;
也就这几个关键字符,那么接下来看如何处理,取出我们想要的key和value呢!

/// <summary>
/// 数据解析
/// </summary>
/// <param name="msg">内容</param>
/// <returns>数据字典kv</returns>
public static Dictionary<string, string> ParseDatatable(string msg)
{
bool isKey = false; //key开始
bool isValue = false; //value开始
bool isValueStart = false; //是否value首次检测
int equalIndex = ; // = 出现次数的计数
int valueStartIndex = ; //Value的起始索引
StringBuilder sbKey = new StringBuilder();
StringBuilder sbvalue = new StringBuilder();
for (int i = ; i < msg.Length; i++)
{
switch (msg[i])
{
case '#':
isKey = false;
if (isValue) //收集颜色码中的#
sbvalue.Append(msg[i]);
break;
case '\r':
continue;
case '\n':
isKey = true;
isValue = false;
if (!string.IsNullOrEmpty(sbKey.ToString()) && !string.IsNullOrEmpty(sbvalue.ToString()))
{
if (infoDic.ContainsKey(sbKey.ToString()))
Debug.LogError(string.Format("[ERROR]:has the same key:{0}, value:{1}", sbKey.ToString(), sbvalue.ToString().Replace("\\n", "\n")));
else
infoDic.Add(sbKey.ToString(), sbvalue.ToString().Replace("\\n","\n"));
}
sbKey.Remove(, sbKey.Length);
sbvalue.Remove(, sbvalue.Length);
break;
case '=':
if (!isValue) //忽略value里的 = 计数
equalIndex++;
if (equalIndex % == && equalIndex > )//key end
{
isKey = false;
isValue = true;
if (valueStartIndex != equalIndex)
{
isValueStart = true;
valueStartIndex = equalIndex;
}
}
if (isValue)
{
if (isValueStart && msg[i - ] == '=')//忽略==value前最开始的那个=
{
isValueStart = false;
continue;
}
sbvalue.Append(msg[i]);
}
break;
default:
if (isKey)
sbKey.Append(msg[i]);
else if (isValue)
{
if (msg[i - ] == '\\' && msg[i + ] == 'n')//忽略转义字符'\'
continue;
sbvalue.Append(msg[i]);
DealEndLine((i == msg.Length - ), ref infoDic, sbKey, sbvalue);
}
break;
}
}
return infoDic;
} /// <summary>
/// 行尾特殊处理
/// </summary>
/// <param name="lastLine">是否最后一行</param>
/// <param name="dictionary">infoDic</param>
/// <param name="key">key</param>
/// <param name="value">value</param>
public static void DealEndLine(bool lastLine, ref Dictionary<string, string> dictionary, StringBuilder key, StringBuilder value)
{
if (lastLine)
{
if (infoDic.ContainsKey(key.ToString()))
Debug.Log(string.Format("[ERROR]:has the same key:{0}, value:{1}", key.ToString(), value.ToString().Replace("\\n", "\n")));
else
infoDic.Add(key.ToString(), value.ToString().Replace("\\n", "\n"));
}
}

这是后期比较完善的代码了,这里做了以下错误兼容:
1,兼容了策划在value里配置==或者===,均不影响解析。
2,兼容了颜色码<color=#7893AA>{1}</color>的’#‘和’=‘,此处不再是特殊转义字符处理。
3,兼容了系统默认会添加"\\n"多个转义字符’\‘导致在Text上无法换行的问题。
4,兼容了策划最后一行无回车换行导致无法解析的bug。
目前就发现以上问题,对以上发现问题进行了解决!
不早了,写这么点东西花了近三个多小时,如果有幸被您读到请留下你的脚印,得洗洗睡了,明天回家了,祝大家十一玩的愉快!!!

PS:“纸上得来终觉浅 绝知此事要躬行”只有在实践中才能发现问题,交流是很好的灵感碰撞,遇到问题了多和小伙伴交流可能会有不一样的解决方案!

传送门:https://gitee.com/wuzhang/UnityParseData.git

Unity 如何高效的解析数据的更多相关文章

  1. 最简单简洁高效的Json数据解析

    一.无图无真相 二.主要代码 1.导入jar包 拷贝fastjson.jar包到projectlibs包下 2.封装工具类JsonUtil.java package com.example.parse ...

  2. 高效的TCP数据拆包器

    高效的TCP数据拆包器 接收器,每秒拆1KB的包达到30万以上 /// 数据同步协义数据接收器 /// </summary> /// <remarks> /// 主要功能有 / ...

  3. scrapy架构与目录介绍、scrapy解析数据、配置相关、全站爬取cnblogs数据、存储数据、爬虫中间件、加代理、加header、集成selenium

    今日内容概要 scrapy架构和目录介绍 scrapy解析数据 setting中相关配置 全站爬取cnblgos文章 存储数据 爬虫中间件和下载中间件 加代理,加header,集成selenium 内 ...

  4. Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制

    本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋 前文回顾 在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网 ...

  5. Android之三种网络请求解析数据(最佳案例)

    AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用. AsyncTask是个抽象类,使用时需要继承这个类,然后调用execute()方法. ...

  6. 在Unity中高效工作(下)

    原地址:http://www.unity蛮牛.com/thread-20005-1-1.html Tips for Creating Better Games and Working More Eff ...

  7. 一起来开发Android的天气软件(四)——使用Gson解析数据

    离上一篇文章过去才4.5天,我们赶紧趁热打铁继续完毕该系列的天气软件的开发. 承接上一章的内容使用Volley实现网络的通信.返回给我们的是这一串Json数据{"weatherinfo&qu ...

  8. Gson解析数据

    package com.bwie.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputS ...

  9. 爬虫系列二(数据清洗--->xpath解析数据)

    一 xpath介绍 XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航. XPath 使用路径表达式在 XML 文档中进行导航 XPath 包 ...

随机推荐

  1. 【Android】3.24 示例24--OpenGL绘制功能

    分类:C#.Android.VS2015.百度地图应用: 创建日期:2016-02-04 一.简介 百度地图SDK为广大开发者开放了OpenGL绘制接口,帮助开发者在地图上实现更灵活的样式绘制,丰富地 ...

  2. C#反射机制学习总结

    反射的定义:审查元数据并收集关于它的类型信息的能力.元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等. Syste ...

  3. HTML onmousedown 事件属性 关于鼠标按键的问题;

    在项目中遇到一个问题,就是点击任意键关闭弹窗的处理:出现了一个bug:就是在angularjs项目中两个controller之间的事件跳转,使用的ng-show =“iscontext” : 如所示: ...

  4. sql插入数据

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. java web hello world(一)

    首先在eclipse 里面创建一个java 动态项目, 记住路径,这里是直接通过根目录直接访问的webContent目录下面 的文件, 创建好后 ,在本地配置Tomcat服务器, 将server加入到 ...

  6. Linux 下 tail 命令

    简述 tail命令从指定点开始将文件写到标准输出,使用tail命令的“-f”选项可以方便的查阅正在改变的日志文件,“tail -f filename”会把filename里最尾部的内容显示在屏幕上,并 ...

  7. 4种用于构建嵌入式linux系统的工具_转

    转自:4种用于构建嵌入式linux系统的工具 Linux 被部署到比 Linus Torvalds 在他的宿舍里开发时所预期的更广泛的设备.令人震惊的支持了各种芯片,使得Linux 可以应用于大大小小 ...

  8. CentOs6.5 安装rabbitmq(转)

    // 安装预环境 yum install gcc gcc-c++ yum install zlib zlin-devel ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 / ...

  9. 为什么手机无法执行应用? Values之谜

    欢迎Follow我的GitHub, 关注我的CSDN, 精彩不断! CSDN: http://blog.csdn.net/caroline_wendy/article/details/68923156 ...

  10. Rails NameError uninitialized constant class solution

    rails nameerror uninitialized constant class will occur if your rails console is not loaded with con ...