引言
大学读的是一个很时髦的专业, 学了四年的游戏竞技. 可惜没学好. 但认真过, 比做什么都认真. 见证了 ......
打的所有游戏人物中 分享一位最喜爱 的
“I've been alone for 10 thousand years. 我孤独了一万年。”
“What I may be,whatever I may become in this world,know that I will always look out of you,Tyrande. 无论我做了什么,无论我变成什么样子,我会永远关心你的,泰兰德。”
前言
上一次 写了两篇 关于 C json 引擎 解析和 构造内容
结合上面加上当前这篇, 一个高效能用的 cjson 引擎就诞生了.
今天 , 分享这篇, 至少可以让大家, 学到简单深拷贝. 一些构造技巧. 还有就是cjson的辅助函数使用!
同样先分享一个函数 封装 开始.
/* * 这个代码是 对 strdup 的再实现, 调用之后需要free * str : 待复制的源码内容 * : 返回 复制后的串内容 */ char* str_dup(const char* str) { int len; char* nstr; DEBUG_CODE({ if (NULL == str) { SL_WARNING("check is NULL == str!!"); return NULL; } }); len = sizeof(char) * (strlen(str) + 1); if (!(nstr = malloc(len))) { SL_FATAL("malloc is error! len = %d.", len); return NULL; } // 返回最后结果 return memcpy(nstr, str, len); }
老代码中 有过 strdup 深拷贝 字符串, 后面各个平台支持的不一样 _strdup __strdup , strdup 各种. 后面为了统一写了一个.
其中 DEBUG_CODE 是对 assert 宏的扩展
/* * 10.1 这里是一个 在 DEBUG 模式下的测试宏 * * 用法 : * DEBUG_CODE({ * puts("debug start..."); * }); */ #ifndef DEBUG_CODE # ifdef _DEBUG # define DEBUG_CODE(code) code # else # define DEBUG_CODE(code) # endif // ! _DEBUG #endif // !DEBUG_CODE
在 _DEBUG模式下开启 测试代码, 没有_DEBUG处于发布状态的时候,这段检测代码编译的时候会被去掉. 为了效率而生.
对于SL_FATAL 是 sclog.h 接口中 一个 内部错误 日志宏. sclog.h 这个日志记录类, 也很不错. 前面博文中
最后那部分 讲解了 sclog.h 代码, 当时讲的的太多了,应该分开细说的. sclog.h 思路还是很不错的, 基本上业界大体也是这个思路.
多用户, 分级等手段. 前言已经介绍完了. 后面就随意了, 都可以不看了.
正文
1. cjson 辅助操作添加的接口
新加的接口如下
// --------------------------------- 下面是 cjson 输出部分的辅助代码 ----------------------------------------- /* * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete * b : bool 值 最好是 _Bool * : 返回 创建好的json 内容 */ extern cjson_t cjson_newnull(); extern cjson_t cjson_newbool(int b); extern cjson_t cjson_newnumber(double vd); extern cjson_t cjson_newstring(const char* vs); extern cjson_t cjson_newarray(void); extern cjson_t cjson_newobject(void); /* * 按照类型,创建 对映类型的数组 cjson对象 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char** * type : 类型目前支持 上面几种类型 * array : 数组原始数据 * len : 数组中元素长度 * : 返回创建的数组对象 */ extern cjson_t cjson_newtypearray(int type, const void* array, int len); /* * 将 jstr中 不需要解析的字符串都去掉 * jstr : 待处理的json串 * : 返回压缩后的json串内容 */ extern char* cjson_mini(char* jstr); /* * 将json文件解析成json内容返回. 需要自己调用 cjson_delete * jpath : json串路径 * : 返回处理好的cjson_t 内容,失败返回NULL */ extern cjson_t cjson_dofile(char* jpath); /* * 在array中分离第idx个索引项内容. * array : 待处理的json_t 数组内容 * idx : 索引内容 * : 返回分离的json_t内容 */ extern cjson_t cjson_detacharray(cjson_t array, int idx); /* * 在object json 中分离 key 的项出去 * object : 待分离的对象主体内容 * key : 关联的键 * : 返回分离的 object中 key的项json_t */ extern cjson_t cjson_detachobject(cjson_t object, const char* key);
前面是创建, 后面是分离, 分离的意思就是离开和上一轮没关系了. 线程也有分离的概念. 分离后自己自行回收.
其中 cjson_mini 是为了去掉 cjson 字符串中 无用的字符内容.
2. 摘录其中优秀的接口,分析设计
首先看看下面一个函数
/* * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平 * jstr : 待处理的json串 * : 返回压缩后的json串内容 */ char* cjson_mini(char* jstr) { char* in = jstr; char* to = jstr; char c; while(!!(c=*to)){ if(sh_isspace(c)) ++to; else if(c == '/' && to[1] == '/') while((c=*++to) && c!='\n'); else if(c == '/' && to[1] == '*'){ while((c=*++to) && !(c=='*' && to[1] =='/')) ; if(c) to+=2; } else if(c=='"'){ *in++ = c; while((c=*++to) && (c!='"' || to[-1]=='\\')) *in++ = c; if(c) { *in++=c; ++to; } } else *in++ = *to++; } *in = '\0'; return jstr; }
思路是 先去掉空格字符, 后面去掉 // 和 /* */ 注释内容. 再对于 "" 包裹的字符串特殊处理. 其它字符原封不动 . 其中 sh_isspace 宏设计
如下
/* * c 如果是空白字符返回 true, 否则返回false * c : 必须是 int 值,最好是 char 范围 */ #define sh_isspace(c) \ ((c==' ')||(c>='\t'&&c<='\r'))
思路也很巧妙. 都是工作总结. 不用谢.
还有一个 构建数组对象代码
/* * 按照类型,创建 对映类型的数组 cjson对象 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char** * type : 类型目前支持 上面几种类型 * array : 数组原始数据 * len : 数组中元素长度 * : 返回创建的数组对象 */ cjson_t cjson_newtypearray(int type, const void* array, int len) { int i; cjson_t n = NULL, p = NULL, a; // _DEBUG 模式下简单检测一下 DEBUG_CODE({ if(type < _CJSON_FALSE || type > _CJSON_STRING || len <=0){ SL_FATAL("check param is error! type = %d, len = %d.", type, len); return NULL; } }); // 这里是实际执行代码 a = cjson_newarray(); for(i=0; i<len; ++i){ switch(type){ case _CJSON_NULL: n = cjson_newnull(); break; case _CJSON_FALSE: n = cjson_newbool(array? ((char*)array)[i] : 0); break; case _CJSON_TRUE: n = cjson_newbool(array? ((char*)array)[i] : 1); break; case _CJSON_NUMBER: n = cjson_newnumber(((double*)array)[i]); break; case _CJSON_STRING: n = cjson_newstring(((char**)array)[i]);break; } if(i){ //有你更好 p->next = n; n->prev = p; } else a->child = n; p = n; } return a; }
其中
cjson_t n = NULL, p = NULL, a;
是为了消除编译器警告, 没有定义就使用未初始化值. 对于其中创建代码都是 普通的 分类型搞. 很大白话.
对于 对象 分离代码如下
/* * 在object json 中分离 key 的项出去 * object : 待分离的对象主体内容 * key : 关联的键 * : 返回分离的 object中 key的项json_t */ cjson_t cjson_detachobject(cjson_t object, const char* key) { cjson_t c; DEBUG_CODE({ if(!object || !object->child || !key || !*key){ SL_WARNING("check param is object:%p, key:%s.", object, key); return NULL; } }); for(c=object->child; c && str_icmp(c->key, key); c=c->next) ; if(!c) { SL_WARNING("check param key:%s => vlaue is empty.", key); return NULL; } if(c->prev) c->prev->next = c->next; if(c->next) c->next->prev = c->prev; if(c == object->child) object->child = c->next; c->prev = c->next = NULL; return c; }
主要是查找, 查找到了 从特殊链表中上传这个对象,重新构建链接关系. 其中 str_icmp 在前面分析过 , 很巧妙
/* * 这是个不区分大小写的比较函数 * ls : 左边比较字符串 * rs : 右边比较字符串 * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0 */ int str_icmp(const char* ls, const char* rs) { int l, r; if(!ls || !rs) return (int)ls - (int)rs; do { if((l=*ls++)>='a' && l<='z') l -= 'a' - 'A'; if((r=*rs++)>='a' && r<='z') r -= 'a' - 'A'; } while(l && l==r); return l-r; }
最后分享 一个 常用函数 , 从json文件中得到cjson_t 对象. 再扯一点, 多写代码,后面熟悉后你会发现, 代码就是最好的注释.
/* * 将json文件解析成json内容返回, 需要自己调用 cjson_delete * jpath : json串路径 * : 返回处理好的cjson_t 内容,失败返回NULL */ cjson_t cjson_dofile(char* jpath) { cjson_t root; tstring tstr = file_malloc_readend(jpath); if(!tstr){ SL_WARNING("readend jpath:%s is error!", jpath); return NULL; } root = cjson_parse(tstr->str); tstring_destroy(&tstr); return root; }
上面关于 tstring 设计 具体看下面博文.
到这里 关于cjson库 补充的 辅助函数重要设计就完毕了.
3. cjson 定稿代码 总展示
cjson.h
#ifndef _H_CJSON #define _H_CJSON // json 中几种数据类型定义 , 对于C而言 最难的是看不见源码,而不是api复杂, 更不是业务复杂 #define _CJSON_FALSE (0) #define _CJSON_TRUE (1) #define _CJSON_NULL (2) #define _CJSON_NUMBER (3) #define _CJSON_STRING (4) #define _CJSON_ARRAY (5) #define _CJSON_OBJECT (6) #define _CJSON_ISREF (256) //set 时候用如果是引用就不释放了 #define _CJSON_ISCONST (512) //set时候用, 如果是const char* 就不释放了 struct cjson { struct cjson *next, *prev; struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空 int type; char *key; // json内容那块的 key名称 char *vs; // type == _CJSON_STRING, 是一个字符串 double vd; // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool }; //定义cjson_t json类型 typedef struct cjson* cjson_t; /* * 这个宏,协助我们得到 int 值 或 bool 值 * * item : 待处理的目标cjson_t结点 */ #define cjson_getint(item) \ ((int)((item)->vd)) /* * 删除json串内容 * c : 待释放json_t串内容 */ extern void cjson_delete(cjson_t* pc); /* * 对json字符串解析返回解析后的结果 * jstr : 待解析的字符串 */ extern cjson_t cjson_parse(const char* jstr); /* * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数 *推荐是数组使用 * array : 待处理的cjson_t数组对象 * : 返回这个数组中长度 */ extern int cjson_getlen(cjson_t array); /* * 根据索引得到这个数组中对象 * array : 数组对象 * idx : 查找的索引 必须 [0,cjson_getlen(array)) 范围内 * : 返回查找到的当前对象 */ extern cjson_t cjson_getarray(cjson_t array, int idx); /* * 根据key得到这个对象 相应位置的值 * object : 待处理对象中值 * key : 寻找的key * : 返回 查找 cjson_t 对象 */ extern cjson_t cjson_getobject(cjson_t object, const char* key); // --------------------------------- 下面是 cjson 输出部分的处理代码 ----------------------------------------- /* * 这里是将 cjson_t item 转换成字符串内容,需要自己free * item : cjson的具体结点 * : 返回生成的item的json串内容 */ extern char* cjson_print(cjson_t item); // --------------------------------- 下面是 cjson 输出部分的辅助代码 ----------------------------------------- /* * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete * b : bool 值 最好是 _Bool * : 返回 创建好的json 内容 */ extern cjson_t cjson_newnull(); extern cjson_t cjson_newbool(int b); extern cjson_t cjson_newnumber(double vd); extern cjson_t cjson_newstring(const char* vs); extern cjson_t cjson_newarray(void); extern cjson_t cjson_newobject(void); /* * 按照类型,创建 对映类型的数组 cjson对象 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char** * type : 类型目前支持 上面几种类型 * array : 数组原始数据 * len : 数组中元素长度 * : 返回创建的数组对象 */ extern cjson_t cjson_newtypearray(int type, const void* array, int len); /* * 将 jstr中 不需要解析的字符串都去掉 * jstr : 待处理的json串 * : 返回压缩后的json串内容 */ extern char* cjson_mini(char* jstr); /* * 将json文件解析成json内容返回. 需要自己调用 cjson_delete * jpath : json串路径 * : 返回处理好的cjson_t 内容,失败返回NULL */ extern cjson_t cjson_dofile(char* jpath); /* * 在array中分离第idx个索引项内容. * array : 待处理的json_t 数组内容 * idx : 索引内容 * : 返回分离的json_t内容 */ extern cjson_t cjson_detacharray(cjson_t array, int idx); /* * 在object json 中分离 key 的项出去 * object : 待分离的对象主体内容 * key : 关联的键 * : 返回分离的 object中 key的项json_t */ extern cjson_t cjson_detachobject(cjson_t object, const char* key); #endif // !_H_CJSON