Unity 如何高效的解析数据
昨天和朋友聊天时,他遇到这么一个问题:现在有按照一定格式的数据,例如:
#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 如何高效的解析数据的更多相关文章
- 最简单简洁高效的Json数据解析
一.无图无真相 二.主要代码 1.导入jar包 拷贝fastjson.jar包到projectlibs包下 2.封装工具类JsonUtil.java package com.example.parse ...
- 高效的TCP数据拆包器
高效的TCP数据拆包器 接收器,每秒拆1KB的包达到30万以上 /// 数据同步协义数据接收器 /// </summary> /// <remarks> /// 主要功能有 / ...
- scrapy架构与目录介绍、scrapy解析数据、配置相关、全站爬取cnblogs数据、存储数据、爬虫中间件、加代理、加header、集成selenium
今日内容概要 scrapy架构和目录介绍 scrapy解析数据 setting中相关配置 全站爬取cnblgos文章 存储数据 爬虫中间件和下载中间件 加代理,加header,集成selenium 内 ...
- Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制
本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋 前文回顾 在前边的系列文章中,我们从内核如何收发网络数据开始以一个C10K的问题作为主线详细从内核角度阐述了网 ...
- Android之三种网络请求解析数据(最佳案例)
AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用. AsyncTask是个抽象类,使用时需要继承这个类,然后调用execute()方法. ...
- 在Unity中高效工作(下)
原地址:http://www.unity蛮牛.com/thread-20005-1-1.html Tips for Creating Better Games and Working More Eff ...
- 一起来开发Android的天气软件(四)——使用Gson解析数据
离上一篇文章过去才4.5天,我们赶紧趁热打铁继续完毕该系列的天气软件的开发. 承接上一章的内容使用Volley实现网络的通信.返回给我们的是这一串Json数据{"weatherinfo&qu ...
- Gson解析数据
package com.bwie.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputS ...
- 爬虫系列二(数据清洗--->xpath解析数据)
一 xpath介绍 XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航. XPath 使用路径表达式在 XML 文档中进行导航 XPath 包 ...
随机推荐
- 【Android】6.0 第6章 对话框--本章示例主界面
分类:C#.Android.VS2015: 创建日期:2016-02-08 在Android应用中,常用的对话框有:Toast.AlertDialog.ProgressDialog.时间选择对话框.日 ...
- 对JAVASCRIPT匿名函数的理解
网上很多解释,我无法理解,我想知道原理...这篇文章应该可以透彻一点 Query片段: (function(){ //这里忽略jQuery所有实现 })(); 要说匿名函数,我们首先要由函数本身说起. ...
- 【转】VMware虚拟机中CentOS设置固定IP
因为需要配置固定IP,在网上找了很久终于找到一个可行的例子,自己配置成功了. 1.首先获取你的GATEWAY 方便后面在cento系统配置里使用选取菜单栏:Edit->Virtual Netwo ...
- django admin 或xdmin list_display search_fields list_filter 如果显示搜索外键或多对多字段
model class UserProfile(AbstractUser): nick_name = models.CharField(max_length=50, verbose_name=u&qu ...
- C++基础学习-20120514
1------指针与引用的区别:1:非空区别.一个引用必须指向某个对象,必须初始化.但是指针可以赋空值,但给指针赋值之前必须制定指针的地址.变量不许为空时必须把变量赋给引用:2:合法性区别.引用使用之 ...
- c# 终止线程
最近在弄一个等待窗口,使用了线程去调用form.在结束线程这边碰到了些问题.调用: thread.Abort();thread.Join();老被ThreadAbortException异常抛出困扰. ...
- java资料——顺序存储结构和链式存储结构(转)
顺序存储结构 主要优点 节省存储空间,随机存取表中元素 缺 点 插入和删除操作需要移动元素 在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构. 顺序存储结 ...
- iOS边练边学--tableView的批量操作
一.tableView批量操作方法一:(自定义) <1>在storyboard中添加imageView控件,用来操作当cell被选中后显示图标 <2>拖线,在自定义控件类中与i ...
- java web中读取properties文件时的路径问题
在web开发时,难免会有一些固定的参数,我们一般把这些固定的参数存在properties文件中,然后用的时候要读出来.但经常出现一些错误,找不到相应的路径,所以,今天特地讲一些如何正确获得路径. 首先 ...
- 使用explain分析sql语句
sql语句优化 : sql语句的时间花在哪儿? 答: 等待时间 , 执行时间. 这两个时间并非孤立的, 如果单条语句执行的快了,对其他语句的锁定的也就少了. 所以,我们来分析如何降低执行时间. : s ...