昨天和朋友聊天时,他遇到这么一个问题:现在有按照一定格式的数据,例如:
#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】5.0 第5章 常用基本控件--本章示例主界面

    分类:C#.Android.VS2015: 创建日期:2016-02-06 这一章主要介绍Android简单控件的基本用法.本章源程序共有9个示例,这些示例都在同一个项目中. 项目名:ch05demo ...

  2. vim学习日志(5):vim下wimrc的配置,解决中文乱码问题

    解决linux下vim乱码的情况:(修改vimrc的内容) 全局的情况下:即所有用户都能用这个配置 文件地址:/etc/vimrc 在文件中添加: ,ucs-bom,gb18030,gbk,gb231 ...

  3. c++ 使用vs2010调用 win32api

    以前读书时都是用vc6.0.后来学c#用vs.装系统只装了vs2010.今天用vs2010写c++程序.发现有点陌生.就总结下,免得以后忘记. 首先用vs2010选择c++语言.新建一个win32控制 ...

  4. 判断IP是否为爬虫IP

    方法一: 通过国外网站验证:http://bot.myip.ms/123.125.71.12 返回结果: IP/Domain - 123.125.71.12:   Baidu Bot on this ...

  5. Tomcat: 8080端口被占用

    用eclipse重启tomcat的时候可能出现这样的错误提示,说明tomcat没有成功地关闭掉. 解决办法:尝试直接用tomcat的shutdown.bat关闭即可,一般都可以解决问题

  6. RTX——第7章 任务管理

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 单任务系统学习多任务系统之前,我们先来回顾下单任务系统的编程框架,即裸机时的编程框架. 裸机编程主要是采 ...

  7. python 读取文件、并以十六进制的方式写入到新文件

    #!/usr/bin/env python infile = file("in.mp3","rb") outfile = file("out.txt& ...

  8. Java Web开发中用Tomcat部署项目的三种方法

    第一种方法:在tomcat中的conf目录中,在server.xml中的,<host/>节点中添加: <Context path="/hello" docBase ...

  9. js中的url地址用function函数调用

    url中输入调用函数,函数中调用ajax请求

  10. Jar/War/Ear等包的作用与区别详解

    以客户角度来看,jar文件就是一种封装格式,用户不需要知道jar包中有多少个.class格式的文件及每个文件中的功能与作用,也可以得到相应的访问的结果.java中除了jar格式还有war和ear等包文 ...