引言

  大学读的是一个很时髦的专业, 学了四年的游戏竞技. 可惜没学好. 但认真过, 比做什么都认真. 见证了  ......

打的所有游戏人物中 分享一位最喜爱 的

  C json实战引擎 三 , 最后实现部分辅助函数

    
    “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 引擎 解析和 构造内容

  C json实战引擎 一 , 实现解析部分

  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 这个日志记录类, 也很不错. 前面博文中

  C 基础框架开发

最后那部分 讲解了 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 设计 具体看下面博文.

  C 封装一个通用链表 和 一个简单字符串开发库

到这里 关于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
View Code

相关文章:

  • 2021-11-29
  • 2022-12-23
  • 2021-06-23
  • 2021-12-31
  • 2022-12-23
  • 2023-01-11
  • 2022-12-23
猜你喜欢
  • 2021-12-20
  • 2021-07-21
  • 2022-02-07
  • 2021-05-01
  • 2021-12-10
  • 2021-07-28
  • 2021-09-17
相关资源
相似解决方案