C json实战引擎 二 , 实现构造部分
引言
这篇博文和前一篇 C json实战引擎一,实现解析部分设计是相同的,都是采用递归下降分析.
这里扯一点 假如你是学生 推荐一本书 给 大家
当然学了上面内容,以后对编译链接设计方面会有很大提高. 但是对于 其它 也没有什么鸟用.
再扯一点 如果想流畅的看完并成功写完上面书中三个案例. 你还需要 看完 市面上关于 C 讲解的所有出名的 有意义的书籍.
编程很简单,勤能补拙, 想提高就会提高. 但成长很难......
风暴 http://music.163.com/#/song?id=211222 陈洁仪
前言
同样,一开始将最有益,最简单的部分代码拿出来供大家分享.
1.从简单 有益代码来
开始分析一段 代码, 先展示一下使用的数据结构
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;
再展示用的 tstring 结构
#ifndef _STRUCT_TSTRING
#define _STRUCT_TSTRING
//简单字符串结构,并定义文本字符串类型tstring
struct tstring {
char* str; //字符串实际保存的内容
int len; //当前字符串大小
int size; //字符池大小
};
typedef struct tstring* tstring;
#endif // !_STRUCT_TSTRING
这些数据结构前面 博文已经对其进行过详细 设计利用分析.
先看一个 double 变成 cjson_t 的算法, 很多细节真的适合 学习尝试用于底层库封装设计中.
// 将item 中值转换成字符串 保存到p中
static char* __print_number(cjson_t item, tstring p)
{
char* str = NULL;
double d = item->vd;
int i = (int)d; if (d == 0) { //普通0
str = __ensure(p, 2);
if (str)
str[0] = '0', str[1] = '\0';
}
else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
str = __ensure(p, 21); //int 值
if (str)
sprintf(str, "%d", i);
}
else {
str = __ensure(p, 64); //double值
if (str) {
double nd = fabs(d); //得到正值开始比较
if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
sprintf(str, "%.0f", d);
else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
sprintf(str, "%e", d);
else
sprintf(str, "%f", d); }
} return str;
}
是不是感觉 很巧妙. 这里 把 int 和 double 都算作 number类型, 出现了 上面算法. 需要导入 #include <float.h> 引用了 DBL_EPSILON 判断是否相等宏阀值.
其中 __ensure 函数 是一个 协助 tstring 分配内存的一个函数
/*
* 这里使用 tstring 结构 size 这里表示 字符串总大小,没有变化
* len 表示当前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的
*/
static char* __ensure(tstring p, int need)
{
char* nbuf;
int nsize;
if (!p || !p->str) {
SL_FATAL("p:%p need:%p is error!", p, need);
return NULL;
}
need += p->len;
if (need <= p->size) //内存够用直接返回结果
return p->str + p->len;
nsize = __pow2gt(need);
if ((nbuf = malloc(nsize*sizeof(char))) == NULL) {
free(p->str);
p->size = p->len = ;
p->str = NULL;
SL_FATAL("malloc nsize = %d error!", nsize);
return NULL;
}
//这里复制内容
memcpy(nbuf, p->str, p->size);
free(p->str);
p->size = nsize;
p->str = nbuf;
return nbuf + p->len;
}
这里采用的 SL_FATAL 日志库, 看我前面博文 如何写一个 高效多用户的 日志库, 特别有用,基本上是开发中标配.
还有一个 __pow2gt(x) 函数技巧, 返回 一个比x 的 n 其中n是2的幂,并且是最小的幂.是一种技巧记住就可以了.估计都是那些写汇编的老代码遗留下来的潜规则吧.
性能没的说. 不明白就当有个印象.
// 2^n>=x , n是最小的整数
static int __pow2gt(int x)
{
--x;
x |= x >> ;
x |= x >> ;
x |= x >> ;
x |= x >> ;
x |= x >> ;
return x + ;
}
到这里 这几个函数 就可以代表这整篇文章了. 后面就可以省略了.
正文
1.开始说 cjson 的 构造
cjson 解析 先认为 所有的都是 一个 value => null or bool or number or string or array or object
其中 array or object 需要再特殊处理,因为其中可能包含 value 即 array or object => value
这样的递归顺序进行的. 这就是传说中的低估下降分析 !!!! 爽不爽 , 当我还是学生的时候,NB任务告诉我学会了 递归了下降分析就可以找个
不错的工作, 找个不错的对象. 现在只想说 呵呵!!.
大概像下面调用关系图
递归嵌套. 好像 Linux 之父 也 说过 去它码的递归.
2.展示 cjson 构造用的接口
这里比较简单,今天只分析 构造部分 接口就一个
// --------------------------------- 下面是 cjson 输出部分的处理代码 ----------------------------------------- /*
* 这里是将 cjson_t item 转换成字符串内容,需要自己free
* item : cjson的具体结点
* : 返回生成的item的json串内容
*/
extern char* cjson_print(cjson_t item);
值得注意的是 上面接口能够将 item变成 char*, 这个char*是堆上分配的. 需要自己 用完后 free.
3. 展示 cjson 部分代码
上面接口构造的函数为
#define _INT_CJONSTR (256)
/*
* 这里是将 cjson_t item 转换成字符串内容,需要自己free
* item : cjson的具体结点
* : 返回生成的item的json串内容
*/
char*
cjson_print(cjson_t item)
{
struct tstring p;
char* out;
if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) {
SL_FATAL("item:%p, p.str = malloc is error!", item);
return NULL;
}
p.size = _INT_CJONSTR;
p.len = ; out = __print_value(item, &p); //从值处理开始, 返回最终结果
if (out == NULL) {
free(p.str);
SL_FATAL("__print_value item:%p, p:%p is error!", item, &p);
return NULL;
}
return realloc(out,strlen(out) + ); // 体积变小 realloc返回一定成功
}
核心 是 __print_value 当然设计方面也参照了一点 cJSON内容. 那我们 继续细说 它
//这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
static char* __print_value(cjson_t item, tstring p);
static char* __print_array(cjson_t item, tstring p);
static char* __print_object(cjson_t item, tstring p); // 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
static char* __print_value(cjson_t item, tstring p)
{
char* out = NULL;
switch ((item->type) & UCHAR_MAX) { // 0xff
case _CJSON_FALSE: if ((out = __ensure(p, ))) strcpy(out, "false"); break;
case _CJSON_TRUE: if ((out = __ensure(p, ))) strcpy(out, "true"); break;
case _CJSON_NULL: if ((out = __ensure(p, ))) strcpy(out, "null"); break;
case _CJSON_NUMBER: out = __print_number(item, p); break;
case _CJSON_STRING: out = __print_string(item->vs, p); break;
case _CJSON_ARRAY: out = __print_array(item, p); break;
case _CJSON_OBJECT: out = __print_object(item, p); break;
} return out;
}
有没有感觉 很自然就是这样的. 上面先声明的 __print_* 系列函数,是为了告诉编译器这个函数地址是什么,方便它能找到 并进入处理.
再展示 其中 __print_object 处理函数, 也很直白
// 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
static char* __print_object(cjson_t item, tstring p)
{
char* ptr;
int i, ncut, len;
cjson_t child = item->child; // 得到孩子结点的深度
for (ncut = ; child; child = child->child)
++ncut;
if (!ncut) {
char* out = NULL;
if (!(out = __ensure(p, )))
strcpy(out, "{}");
return out;
} i = p->len;
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = '{';
*ptr -= '\0';
p->len += ;
// 根据子结点 处理
for (child = item->child; (child); child = child->next) {
__print_string(child->key, p);
p->len = __update(p); //加入一个冒号
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = ':';
p->len += ; //继续打印一个值
__print_value(child, p);
p->len = __update(p); //结算最后内容
len = child->next ? : ;
if ((ptr = __ensure(p, len + )) == NULL)
return NULL;
if (child->next)
*ptr++ = ',';
*ptr = '\0';
p->len += len;
}
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = '}';
*ptr = '\0';
return p->str + i;
}
先处理key ,后面value 用 __print_value 处理. 到这里 基本思路都有了,其它是靠你自己努力临摹 把键盘敲烂!
完整部分代码如下
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
* b : bool 值 最好是 _Bool
* : 返回 创建好的json 内容
*/
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
* 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内容返回
* jpath : json串路径
* : 返回处理好的cjson_t 内容,失败返回NULL
*/
extern cjson_t cjson_dofile(char* jpath); #endif // !_H_CJSON
cjson.c
#include <cjson.h>
#include <schead.h>
#include <sclog.h>
#include <tstring.h>
#include <float.h>
#include <math.h> // 删除cjson
static void __cjson_delete(cjson_t c)
{
cjson_t next;
while (c) {
next = c->next;
//递归删除儿子
if (!(c->type & _CJSON_ISREF)) {
if (c->child) //如果不是尾递归,那就先递归
__cjson_delete(c->child);
if (c->vs)
free(c->vs);
}
else if (!(c->type & _CJSON_ISCONST) && c->key)
free(c->key);
free(c);
c = next;
}
} /*
* 删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c : 待释放json_t串内容
*/
void
cjson_delete(cjson_t* pc)
{
if (!pc || !*pc)
return;
__cjson_delete(*pc);
*pc = NULL;
} //构造一个空 cjson 对象
static inline cjson_t __cjson_new(void)
{
cjson_t c = calloc(, sizeof(struct cjson));
if (!c) {
SL_FATAL("calloc sizeof struct cjson error!");
exit(_RT_EM);
}
return c;
} // 简化的代码段,用宏来简化代码书写 , 16进制处理
#define __parse_hex4_code(c, h) \
if (c >= '' && c <= '') \
h += c - ''; \
else if (c >= 'A' && c <= 'F') \
h += + c - 'A'; \
else if (c >= 'a' && c <= 'z') \
h += + c - 'F'; \
else \
return // 等到unicode char代码
static unsigned __parse_hex4(const char* str)
{
unsigned h = ;
char c = *str;
//第一轮
__parse_hex4_code(c, h);
h <<= ;
c = *++str;
//第二轮
__parse_hex4_code(c, h);
h <<= ;
c = *++str;
//第三轮
__parse_hex4_code(c, h);
h <<= ;
c = *++str;
//第四轮
__parse_hex4_code(c, h); return h;
} // 分析字符串的子函数,
static const char* __parse_string(cjson_t item, const char* str)
{
static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
const char *ptr;
char *nptr, *out;
int len;
char c;
unsigned uc, nuc; if (*str != '\"') { // 检查是否是字符串内容
SL_WARNING("need \\\" str => %s error!", str);
return NULL;
} for (ptr = str + , len = ; (c = *ptr++) != '\"' && c; ++len)
if (c == '\\') //跳过转义字符
++ptr;
if (!(out = malloc(len + ))) {
SL_FATAL("malloc %d size error!", len + );
return NULL;
}
// 这里复制拷贝内容
for (ptr = str + , nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
if (c != '\\') {
*nptr++ = c;
continue;
}
// 处理转义字符
switch ((c = *++ptr)) {
case 'b': *nptr++ = '\b'; break;
case 'f': *nptr++ = '\f'; break;
case 'n': *nptr++ = '\n'; break;
case 'r': *nptr++ = '\r'; break;
case 't': *nptr++ = '\t'; break;
case 'u': // 将utf16 => utf8, 专门的utf处理代码
uc = __parse_hex4(ptr + );
ptr += ;//跳过后面四个字符, unicode
if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == ) break; /* check for invalid. */ if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[] != '\\' || ptr[] != 'u')
break; /* missing second-half of surrogate. */
nuc = __parse_hex4(ptr + );
ptr += ;
if (nuc < 0xDC00 || nuc>0xDFFF)
break; /* invalid second-half of surrogate. */
uc = 0x10000 + (((uc & 0x3FF) << ) | (nuc & 0x3FF));
} len = ;
if (uc < 0x80)
len = ;
else if (uc < 0x800)
len = ;
else if (uc < 0x10000)
len = ;
nptr += len; switch (len) {
case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
case : *--nptr = (uc | __marks[len]);
}
nptr += len;
break;
default: *nptr++ = c;
}
} *nptr = '\0';
if (c == '\"')
++ptr;
item->vs = out;
item->type = _CJSON_STRING;
return ptr;
} // 分析数值的子函数,写的可以
static const char* __parse_number(cjson_t item, const char* str)
{
double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
int e = , es = ; //e表示后面指数, es表示 指数的正负,负为-1
char c; if ((c = *str) == '-' || c == '+') {
ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
++str;
}
//处理整数部分
for (c = *str; c >= '' && c <= ''; c = *++str)
n = n * + c - '';
if (c == '.')
for (; (c = *++str) >= '' && c <= ''; --nd)
n = n * + c - ''; // 处理科学计数法
if (c == 'e' || c == 'E') {
if ((c = *++str) == '+') //处理指数部分
++str;
else if (c == '-')
es = -, ++str;
for (; (c = *str) >= '' && c <= ''; ++str)
e = e * + c - '';
} //返回最终结果 number = +/- number.fraction * 10^+/- exponent
n = ns * n * pow(10.0, nd + es * e);
item->vd = n;
item->type = _CJSON_NUMBER;
return str;
} // 跳过不需要处理的字符
static const char* __skip(const char* in)
{
if (in && *in && *in <= ) {
unsigned char c;
while ((c = *++in) && c <= )
;
}
return in;
} // 递归下降分析 需要声明这些函数
static const char* __parse_array(cjson_t item, const char* str);
static const char* __parse_object(cjson_t item, const char* str);
static const char* __parse_value(cjson_t item, const char* value); // 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
cjson_t child;
if (*str != '[') {
SL_WARNING("array str error start: %s.", str);
return NULL;
} item->type = _CJSON_ARRAY;
str = __skip(str + );
if (*str == ']') // 低估提前结束
return str + ; item->child = child = __cjson_new();
str = __skip(__parse_value(child, str));
if (!str) {//解析失败 直接返回
SL_WARNING("array str error e n d one: %s.", str);
return NULL;
}
while (*str == ',') {
cjson_t nitem = __cjson_new();
child->next = nitem;
nitem->prev = child;
child = nitem;
str = __skip(__parse_value(child, __skip(str + )));
if (!str) {// 写代码是一件很爽的事
SL_WARNING("array str error e n d two: %s.", str);
return NULL;
}
} if (*str != ']') {
SL_WARNING("array str error e n d: %s.", str);
return NULL;
}
return str + ; // 跳过']'
} // 分析对象的子函数
static const char* __parse_object(cjson_t item, const char* str)
{
cjson_t child;
if (*str != '{') {
SL_WARNING("object str error start: %s.", str);
return NULL;
} item->type = _CJSON_OBJECT;
str = __skip(str + );
if (*str == '}')
return str + ; //处理结点, 开始读取一个 key
item->child = child = __cjson_new();
str = __skip(__parse_string(child, str));
if (!str || *str != ':') {
SL_WARNING("__skip __parse_string is error : %s!", str);
return NULL;
}
child->key = child->vs;
child->vs = NULL; str = __skip(__parse_value(child, __skip(str + )));
if (!str) {
SL_WARNING("__skip __parse_string is error 2!");
return NULL;
} // 递归解析
while (*str == ',') {
cjson_t nitem = __cjson_new();
child->next = nitem;
nitem->prev = child;
child = nitem;
str = __skip(__parse_string(child, __skip(str + )));
if (!str || *str != ':'){
SL_WARNING("__parse_string need name or no equal ':' %s.", str);
return NULL;
}
child->key = child->vs;
child->vs = NULL; str = __skip(__parse_value(child, __skip(str+)));
if (!str) {
SL_WARNING("__parse_string need item two ':' %s.", str);
return NULL;
}
} if (*str != '}') {
SL_WARNING("object str error e n d: %s.", str);
return NULL;
}
return str + ;
} // 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
char c;
if ((value) && (c = *value)) {
switch (c) {
// n = null, f = false, t = true
case 'n' : return item->type = _CJSON_NULL, value + ;
case 'f' : return item->type = _CJSON_FALSE, value + ;
case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + ;
case '\"': return __parse_string(item, value);
case '' : case '': case '': case '': case '': case '': case '': case '': case '': case '':
case '+' : case '-': return __parse_number(item, value);
case '[' : return __parse_array(item, value);
case '{' : return __parse_object(item, value);
}
}
// 循环到这里是意外 数据
SL_WARNING("params value = %s!", value);
return NULL;
} /*
* 对json字符串解析返回解析后的结果
* jstr : 待解析的字符串
* : 返回解析好的字符串内容
*/
cjson_t
cjson_parse(const char* jstr)
{
cjson_t c = __cjson_new();
const char* end; if (!(end = __parse_value(c, __skip(jstr)))) {
SL_WARNING("__parse_value params end = %s!", end);
cjson_delete(&c);
return NULL;
} //这里是否检测 返回测试数据
return c;
} /*
* 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
*推荐是数组使用
* array : 待处理的cjson_t数组对象
* : 返回这个数组中长度
*/
int
cjson_getlen(cjson_t array)
{
int len = ;
if (array)
for (array = array->child; array; array = array->next)
++len; return len;
} /*
* 根据索引得到这个数组中对象
* array : 数组对象
* idx : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
* : 返回查找到的当前对象
*/
cjson_t
cjson_getarray(cjson_t array, int idx)
{
cjson_t c;
DEBUG_CODE({
if (!array || idx < ) {
SL_FATAL("array:%p, idx=%d params is error!", array, idx);
return NULL;
}
}); for (c = array->child; c&&idx > ; c = c->next)
--idx; return c;
} /*
* 根据key得到这个对象 相应位置的值
* object : 待处理对象中值
* key : 寻找的key
* : 返回 查找 cjson_t 对象
*/
cjson_t
cjson_getobject(cjson_t object, const char* key)
{
cjson_t c;
DEBUG_CODE({
if (!object || !key || !*key) {
SL_FATAL("object:%p, key=%s params is error!", object, key);
return NULL;
}
}); for (c = object->child; c && str_icmp(key, c->key); c = c->next)
; return c;
} // --------------------------------- 下面是 cjson 输出部分的处理代码 ----------------------------------------- // 2^n>=x , n是最小的整数
static int __pow2gt(int x)
{
--x;
x |= x >> ;
x |= x >> ;
x |= x >> ;
x |= x >> ;
x |= x >> ;
return x + ;
} /*
* 这里使用 tstring 结构 size 这里表示 字符串总大小,没有变化
* len 表示当前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的
*/
static char* __ensure(tstring p, int need)
{
char* nbuf;
int nsize;
if (!p || !p->str) {
SL_FATAL("p:%p need:%p is error!", p, need);
return NULL;
}
need += p->len;
if (need <= p->size) //内存够用直接返回结果
return p->str + p->len;
nsize = __pow2gt(need);
if ((nbuf = malloc(nsize*sizeof(char))) == NULL) {
free(p->str);
p->size = p->len = ;
p->str = NULL;
SL_FATAL("malloc nsize = %d error!", nsize);
return NULL;
}
//这里复制内容
memcpy(nbuf, p->str, p->size);
free(p->str);
p->size = nsize;
p->str = nbuf;
return nbuf + p->len;
} // 这里更新一下 当前字符串, 返回当前字符串的长度
inline static int __update(tstring p)
{
return (!p || !p->str) ? : p->len + strlen(p->str+p->len);
} // 将item 中值转换成字符串 保存到p中
static char* __print_number(cjson_t item, tstring p)
{
char* str = NULL;
double d = item->vd;
int i = (int)d; if (d == ) { //普通0
str = __ensure(p, );
if (str)
str[] = '', str[] = '\0';
}
else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
str = __ensure(p, ); //int 值
if (str)
sprintf(str, "%d", i);
}
else {
str = __ensure(p, ); //double值
if (str) {
double nd = fabs(d); //得到正值开始比较
if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
sprintf(str, "%.0f", d);
else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
sprintf(str, "%e", d);
else
sprintf(str, "%f", d); }
} return str;
} // 输出字符串内容
static char* __print_string(char* str, tstring p)
{
const char* ptr;
char *nptr, *out;
int len = , flag = ;
unsigned char c; if (!str || !*str) { //最特殊情况,什么都没有 返回NULL
out = __ensure(p, );
if (!out)
return NULL;
out[] = '\"', out[] = '\"', out[] = '\0';
return out;
} for (ptr = str; (c=*ptr); ++ptr)
flag |= ((c > && c < ) || c == '\"' || c == '\\'); if (!flag) { //没有特殊字符直接处理结果
len = ptr - str;
out = __ensure(p,len + );
if (!out)
return NULL;
nptr = out;
*nptr++ = '\"';
strcpy(nptr, str);
nptr[len] = '\"';
nptr[len + ] = '\0';
return out;
} //处理 存在 "和转义字符内容
for (ptr = str; (c = *ptr) && ++len; ++ptr) {
if (strchr("\"\\\b\f\n\r\t", c))
++len;
else if (c < ) //隐藏字符的处理, 这里可以改
len += ;
} if ((out = __ensure(p, len + )) == NULL)
return NULL;
//先添加 \"
nptr = out;
*nptr++ = '\"';
for (ptr = str; (c = *ptr); ++ptr) {
if (c > && c != '\"' && c != '\\') {
*nptr++ = c;
continue;
}
*nptr++ = '\\';
switch (c){
case '\\': *nptr++ = '\\'; break;
case '\"': *nptr++ = '\"'; break;
case '\b': *nptr++ = 'b'; break;
case '\f': *nptr++ = 'f'; break;
case '\n': *nptr++ = 'n'; break;
case '\r': *nptr++ = 'r'; break;
case '\t': *nptr++ = 't'; break;
default: sprintf(nptr, "u%04x", c);nptr += ; /* 不可见字符 采用 4字节字符编码 */
}
}
*nptr++ = '\"';
*nptr = '\0';
return out;
} //这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
static char* __print_value(cjson_t item, tstring p);
static char* __print_array(cjson_t item, tstring p);
static char* __print_object(cjson_t item, tstring p); // 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
static char* __print_value(cjson_t item, tstring p)
{
char* out = NULL;
switch ((item->type) & UCHAR_MAX) { // 0xff
case _CJSON_FALSE: if ((out = __ensure(p, ))) strcpy(out, "false"); break;
case _CJSON_TRUE: if ((out = __ensure(p, ))) strcpy(out, "true"); break;
case _CJSON_NULL: if ((out = __ensure(p, ))) strcpy(out, "null"); break;
case _CJSON_NUMBER: out = __print_number(item, p); break;
case _CJSON_STRING: out = __print_string(item->vs, p); break;
case _CJSON_ARRAY: out = __print_array(item, p); break;
case _CJSON_OBJECT: out = __print_object(item, p); break;
} return out;
} // 同样 假定 item 和 p都是存在且不为NULL
static char* __print_array(cjson_t item, tstring p)
{
char* ptr;
cjson_t child = item->child;
int ncut, i;
// 得到孩子结点的深度
for (ncut = ; (child); child = child->child)
++ncut;
if (!ncut) { //没有孩子结点 直接空数组返回结果
char* out = NULL;
if (!(out = __ensure(p, )))
strcpy(out, "[]");
return out;
} i = p->len;
if (!(ptr = __ensure(p, )))
return NULL;
*ptr = '[';
++p->len;
for (child = item->child; (child); child = child->next) {
__print_value(child, p);
p->len = __update(p);
if (child->next) {
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = ',';
*ptr = '\0';
p->len += ;
}
}
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = ']';
*ptr = '\0';
return p->str + i; } // 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
static char* __print_object(cjson_t item, tstring p)
{
char* ptr;
int i, ncut, len;
cjson_t child = item->child; // 得到孩子结点的深度
for (ncut = ; child; child = child->child)
++ncut;
if (!ncut) {
char* out = NULL;
if (!(out = __ensure(p, )))
strcpy(out, "{}");
return out;
} i = p->len;
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = '{';
*ptr -= '\0';
p->len += ;
// 根据子结点 处理
for (child = item->child; (child); child = child->next) {
__print_string(child->key, p);
p->len = __update(p); //加入一个冒号
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = ':';
p->len += ; //继续打印一个值
__print_value(child, p);
p->len = __update(p); //结算最后内容
len = child->next ? : ;
if ((ptr = __ensure(p, len + )) == NULL)
return NULL;
if (child->next)
*ptr++ = ',';
*ptr = '\0';
p->len += len;
}
if (!(ptr = __ensure(p, )))
return NULL;
*ptr++ = '}';
*ptr = '\0';
return p->str + i;
} #define _INT_CJONSTR (256)
/*
* 这里是将 cjson_t item 转换成字符串内容,需要自己free
* item : cjson的具体结点
* : 返回生成的item的json串内容
*/
char*
cjson_print(cjson_t item)
{
struct tstring p;
char* out;
if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) {
SL_FATAL("item:%p, p.str = malloc is error!", item);
return NULL;
}
p.size = _INT_CJONSTR;
p.len = ; out = __print_value(item, &p); //从值处理开始, 返回最终结果
if (out == NULL) {
free(p.str);
SL_FATAL("__print_value item:%p, p:%p is error!", item, &p);
return NULL;
}
return realloc(out,strlen(out) + ); // 体积变小 realloc返回一定成功
} // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
到这里基本前期主场都完了. 后面就是玩测试了.
4. 展示 cjson 测试部分
首先展示 test_cjson_write.c 测试脚本
#include <schead.h>
#include <sclog.h>
#include <cjson.h> // 测试 cjson 函数
int main(int argc, char* argv[])
{
//注册等待函数
INIT_PAUSE(); //启动日志记录功能
sl_start(); // 测试json 串
char jstr[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":[1, 3, 4, 5.66], \n\"height\": 1080, \n\"interlace\": false}\n}"; printf("源码串 :\n %s\n", jstr); // 先生成 json 对象
cjson_t root = cjson_parse(jstr);
if (root == NULL) {
puts("jstr 解析失败! 程序退出中....");
exit(EXIT_FAILURE);
} //这里简单测试输出内容
char* njstr = cjson_print(root); if (njstr == NULL) {
puts("输出内容失败,程序退出中!");
cjson_delete(&root);
exit(EXIT_FAILURE);
} //合法范围直接输出 内容
printf("解析串 :\n %s\n", njstr); //解析完需要释放
free(njstr); //解析好 一定要注意释放操作
cjson_delete(&root); //另一个测试 输出内存值
printf("d = %d\n", strlen("{\"name\":\"Jack (\\\"Bee\\\") Nimble\",\"format\":{\"type\":[1,3,4,5.660000],\"height\":1080,\"interlace\":false}}"));
}
需要大家写一遍或看三遍. 测试结果 如下:
一切正常. 这里输出是可以的. 欢迎大家尝试了.
5.下次来个 cjson 较完整demo
下次写好这个cjson库, 我们 测试一个下面 json 文件 实战解析一下
firefighting_rule.json
{
"firefighting_rule":
{
"key1":
{
"id":,
"dungeon_id":,
"level_contain":[,,,,,,,,,,,,,,],
"active_time":[[,""],[,""],[,""]],
"boss_ui_head":"UI_icon/IMG_ShiJieBoss_TouXiang.png",
"activity_tag_icon":"IMG_GaiBan_HuoDong_ShiJieBoss_TuBiao.png",
"activity_tag_word":"IMG_GaiBan_ZhuCheng_ShiJieBoss_TuBiao_MingZi.png",
"activity_pic_json":"UI_HuoDong_ShiJieBoss.json",
"jinbi_buff_icon":"UI_icon/IMG_WorldBoss_JinbiBuff_Atk.png",
"jinbi_buff_damage":[[,],[,],[,],[,]],
"jinbi_buff_price":,
"jinbi_buff_limit":,
"free_change":,
"refresh_price":,
"change_price":,
"show_hero_num":
}
}
}
解析成我们想要的那样的东西. 尝试, 追求, 按部就班 , 人生...........................................
后记
错误是难免,欢迎批评指正. 下次会对cjson 进行简单总结, 再添加一些辅助函数. 一切会从简单来.
C json实战引擎 二 , 实现构造部分的更多相关文章
- c json实战引擎四 , 最后❤跳跃
引言 - 以前那些系列 长活短说, 写的最终 scjson 纯c跨平台引擎, 希望在合适场景中替代老的csjon引擎, 速度更快, 更轻巧. 下面也是算一个系列吧. 从cjson 中得到灵感, 外加 ...
- C json实战引擎 三 , 最后实现部分辅助函数
引言 大学读的是一个很时髦的专业, 学了四年的游戏竞技. 可惜没学好. 但认真过, 比做什么都认真. 见证了 ...... 打的所有游戏人物中 分享一位最喜爱 的 “I've been alone ...
- c json实战引擎五 , 优化重构
引言 scjson是一个小巧的纯c跨平台小巧引擎. 适用于替换老的cJSON引擎的场景. 数据结构和代码布局做了大量改进.优势体现在以下几个方面: 1) 跨平台 (window 10 + VS2017 ...
- C json实战引擎 一 , 实现解析部分
引言 以前可能是去年的去年,写了一个 c json 解析引擎用于一个统计实验数据项目开发中. 基本上能用. 去年在网上 看见了好多开源的c json引擎 .对其中一个比较标准的 cJSON 引擎 深入 ...
- c json实战引擎六 , 感觉还行
前言 看到六, 自然有 一二三四五 ... 为什么还要写呢. 可能是它还需要活着 : ) 挣扎升级中 . c json 上面代码也存在于下面项目中(维护的最及时) structc json 这次版本 ...
- [CXF REST标准实战系列] 二、Spring4.0 整合 CXF3.0,实现测试接口(转)
转自:[CXF REST标准实战系列] 二.Spring4.0 整合 CXF3.0,实现测试接口 文章Points: 1.介绍RESTful架构风格 2.Spring配置CXF 3.三层初设计,实现W ...
- 响应性web设计实战总结(二)
响应性web设计实战总结(二) 阅读目录 背景知识: Gulp-less安装及配置如下 对响应性web总结,之前总结过2篇文章:可以看如下: http://www.cnblogs.com/tugenh ...
- python机器学习实战(二)
python机器学习实战(二) 版权声明:本文为博主原创文章,转载请指明转载地址 http://www.cnblogs.com/fydeblog/p/7159775.html 前言 这篇noteboo ...
- Mybaits-plus实战(二)
1. Mybaits-plus实战(二) 1.1. mybatis-plus插件 1.1.1. 用法 先举个例子介绍用法,如下:直接作为Bean注入,一般来讲插件太多印象性能,所以大部分插件都只在测试 ...
随机推荐
- 【bzoj1782】[Usaco2010 Feb]slowdown 慢慢游 树链剖分+线段树
题目描述 每天Farmer John的N头奶牛(1 <= N <= 100000,编号1…N)从粮仓走向他的自己的牧场.牧场构成了一棵树,粮仓在1号牧场.恰好有N-1条道路直接连接着牧场, ...
- POJ2299:Ultra-QuickSort——题解
http://poj.org/problem?id=2299 题目大意:给一串数,求其按照两两交换排序最少排几次. 求逆序对裸题,不建议用数据结构(因为需要离散化) #include<cstdi ...
- BZOJ3714 [PA2014]Kuglarz 【最小生成树】
题目链接 BZOJ3714 题解 我们如果知道了所有的数,同样就知道了所有的前缀和 相反,我们如果求出了所有前缀和,就知道了所有的数,二者是等价的 对于一个区间\([l,r]\)如果我们知道了前缀和\ ...
- BZOJ3495 PA2010 Riddle 【2-sat】
题目链接 BZOJ3495 题解 每个城市都有选和不选两种情况,很容易考虑到2-sat 边的限制就很好设置了,主要是每个郡只有一个首都的限制 我们不可能两两之间连边,这样复杂度就爆炸了 于是乎就有了一 ...
- @RequestParam 注解的使用
@RequestParam 注解的使用 前言 在SpringMvc后台进行获取数据,一般是两种. 1.request.getParameter(“参数名”) 2.用@RequestParam注解获取 ...
- bnuoj25660 Two Famous Companies
题目链接:https://www.bnuoj.com/v3/problem_show.php?pid=25660 这个二分真的是烧脑QAQ,想了一晚上才懂了一个大概. 首先,整体思路是二分,直观上感受 ...
- bzoj 相似回文串 3350 3103 弦图染色+manacher
相似回文串 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 143 Solved: 68[Submit][Status][Discuss] Descr ...
- POJ3186 DP
Treats for the Cows Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5753 Accepted: 29 ...
- Qt ---------- connect连接类型
Qt::AutoConnection 0 (Default) If the receiver lives in the thread that emits the signal, Qt::Direct ...
- STM32串口发送第一个字符丢失解决之道
stm32用printf函数重定向到串口USART1发现第一个字符没打印出来具体如下: 刚开始修改fputc函数如下: int fputc(int ch,FILE *f){USART_SendData ...