昨天和朋友聊天时,他遇到这么一个问题:现在有按照一定格式的数据,例如:
#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. 普通用户su 到root,无需密码方式,及iptables封掉本机某个端口,core文件配置

    一. 普通用户su到root无需密码: 随着服务器越来越多,普通用户转到root下,去查密码表是个很繁琐的事,发现有如下方式比较方便(需要root操作) vi /etc/pam.d/su  将 aut ...

  2. 常用的正则表达式C#工具类

    Regex类实现了一些特殊功能数据检查,正则表达式的一些常用的功能,集成至该类中. public class RegexDao { private RegexDao() { } private sta ...

  3. GNU风格 ARM汇编语法1

    汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等. 这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.GNU汇编语言语句格式 任何Linux汇编行 ...

  4. vim复制粘贴常用命令

    在Windows下我们习惯的操作,复制单个字符,复制单行多行,删除单行多行,在linux的vim中操作如下: G(shift+g+g):跳到文档尾 g+g:跳转到文档首 home键:光标移动到行首 e ...

  5. How to Use HTML5 FUll Screen API(如何使用HTML5全屏接口) 【精】

    原文链接:http://www.sitepoint.com/use-html5-full-screen-api/ 如果你不太喜欢变化太快的东西,那么web开发可能不适合你.我曾在2012年末有写过Fu ...

  6. java原生序列化和Kryo序列化性能比较

    简介 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf,Thrift, ...

  7. 【Unity/C#】DateTime时间字符串,月份用英文显示

    制作一个钟表,要求效果如下图: 由于每一部分的字体大小不同,我分别使用了不同的Text控件.(不懂dalao们有没有更科学的办法) 把这些Text控件包含在一个Object下,给该Object定义一个 ...

  8. python-can 的使用

    最近在搞 websocket, 服务端是用 python 写的,所以,我需要用python 控制 can 去传输相关信息. python-can 模块就是 python 控制 can 的模仿. 利用 ...

  9. kubernetes健康检查

    有时候容器在running的状态,但是里面的服务挂了,这个就难办了,所以k8s提供了一种检查服务是否健康的方法 Liveness Probe的种类: ● ExecAction:在container中执 ...

  10. SpringMVC 多个数据源 配置多个事物管理器 Multiple Transaction Managers

    http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction ...