CJSON源码研究笔记
断断续续的cjson看了也有一段时间了,研究一番还是收获颇多!很适合有一点c基础的想继续提高练手的开源源码!cjson.c代码只有700多行,官网上下的,代码风格个人感觉不是很方便,如果全部展开的话代码估计至少不在1100行之下。网上也看了一些前辈们的cjson笔记!对于像我这这样初次接触cjson还是相当有帮助的!下面就来一点一点的分析源码!这里记录一下自己对源码研究理解的笔记!同时也希望对别人作为参考也有一点点的帮助!
研究源码之前首先还是搞清楚cjson到底是干啥的!这样可以对整个源码有个大体的把握!下面是举一些例子可以大致了解一下what is cjson?
cjson:
解释一:
json(javascript object notation) 是一种轻量级的数据交换格式,主要用于传送数据。json 可以将 javascript 对象中表示的一组数据转换为字符串,然后就可以在函数之间轻松地传递这个字符串,或者在异步应用程序中将字符串从 web 客户机传递给服务器端程序。这个字符串看起来有点儿古怪,但是 javascript 很容易解释它,而且 json 可以表示比"名称 / 值对"更复杂的结构。例如,可以表示数组和复杂的对象,而不仅仅是键和值的简单列表。json采用完全独立于语言的文本格式,但是也使用了类似于c语言家族的习惯(包括c, c++, c#, java, javascript, perl,python等)。这些特性使json成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成。
解释二:
就是一种数据格式,不必过于纠结于此。就像一种交通工具,就像你上班要开车一样,可能骑自行车也是一种交通工具,但是json这种交通工具更方便,更快捷。
解释三:
对于cjson的使用,我主要是用来模拟远程服务器端返回的一个json类型的目录结构,客户端进行获取并进行解析,把解析出来的目录按照原本的结构显示在本地
当然除了上面的一些解释最权威的还是cjson官方解释!这个自己直接搜,百度谷歌!上面不仅有可供下载的各种语言版本的源码,也有很具体的介绍!下面就来逐步深入的分析研究源码了!(源码我是用si来看的)
#include #include #include #include #include #include #include #include "cjson.h" static const char *global_ep; const char *cjson_geterrorptr(void) {return global_ep;} static int cjson_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1; if (!s2) return 1; for(; (*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); }从头文件下的第一行代码开始,定义了一个全局静态字符指针变量,该指针指向一个常量(即指向的数据为常量).然后下面一行代码就使用了这个指针变量,搜索global_eq可以看到这个变量在整个.c文件中只出现在三个地方:
除了上面两个地方,还有一个就是上图中第四个箭头所指的地方,
先来看看:
const char *cjson_geterrorptr(void) {return global_ep;},
/* if you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. if not, then cjson_geterrorptr() does the job. */
这个函数可以看下.h文件中的英文注释,这个函数在官方的test.c中有一次调用!
if (!json) {printf("error before: [%s]\n",cjson_geterrorptr());}这个短短一行代码的函数其实就是当传入的字符串解析失败就返回解析失败处的地址后边的字符串!
下面再来逐行分析下面的代码:
函数:int tolower(int c);
函数说明:若参数 c 为大写字母则将该对应的小写字母返回。
返回值:返回转换后的小写字母,若不须转换则将参数c 值返回。
函数说明
cjson_strcasecmp()用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异。返回值 若参数s1和s2字符串相同则返回0。s1长度大于s2长度则返回大于0 的值,s1 长度若小于s2 长度则返回小于0的值.(对照着下面源码分析应该已经很清晰了)
static int cjson_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1; //if语句里面的我习惯这样写if(s1 == null) 不过这里这样写感觉逼格要高一点的样子 if (!s2) return 1; for(; (*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); }首先对传入的参数做合法性检测,这个api的功能就是比较参数s1和s2字符串,当初看到这个for循环下面的一行代码时比较不理解为什么用if(*s1 == 0)作为这个for循环的一个出口!字符串结束符不是'\0'吗?难道tolower函数在遇到结束符'\0'后返回的是0?搞明白了之后发现这里原来是不按套路出牌!
而在ascii码表中,null 就是0!
顺便查了下c库中的strcasecmp函数是这么实现的:
int strcasecmp(const char *s1, const char *s2) { int c1, c2; do { c1 = tolower(*s1++); c2 = tolower(*s2++); } while(c1 == c2 && c1 != 0); return c1 - c2; }
static void *(*cjson_malloc)(size_t sz) = malloc;//定义一个函数指针并初始化指向malloc函数 static void (*cjson_free)(void *ptr) = free;//同上,这里有一个很巧妙灵活的功能,下边会提到
//将传入的字符串复制一副本并返回新的字符串指针
static char* cjson_strdup(const char* str) { size_t len; char* copy; len = strlen(str) + 1; if (!(copy = (char*)cjson_malloc(len))) return 0; memcpy(copy,str,len); return copy; }
//hook内存管理函数,默认申请、释放内存函数malloc、free 也可以自定内存管理函数,增加灵活度,顺便这里的三目运算符还有函数指针结合用在这里简直是大写的赞!(代码的安全考虑的也比较全)
void cjson_inithooks(cjson_hooks* hooks) { if (!hooks) { /* reset hooks */ //如果hooks为空,使用默认的内存管理 cjson_malloc = malloc; cjson_free = free; return; } cjson_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cjson_free = (hooks->free_fn)?hooks->free_fn:free; }这里顺便贴上cjson_hook结构体定义:
typedef struct cjson_hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cjson_hooks;
//内存管理函数,new一个cjson节点(对象)出来,并返回指向该节点的地址, 注意返回类型为(cjson *)型
/* internal constructor. */ static cjson *cjson_new_item(void) { cjson* node = (cjson*)cjson_malloc(sizeof(cjson));//malloc出一个节点 if (node) memset(node,0,sizeof(cjson));//将内存初始化为0 return node; }这里顺便说一下cjson结构体的类型:
/* the cjson structure: */ typedef struct cjson { struct cjson *next,*prev; //双向链表指针 struct cjson *child; //第一个儿子的指针 这个后边用到会具体说道 int type; /* the type of the item, as above. */ char *valuestring; /* the item's string, if type==cjson_string */ int valueint; /* the item's number, if type==cjson_number */ double valuedouble; /* the item's number, if type==cjson_number */ char *string; //如果是对象的key_value元素的话, key值 } cjson;
type:
/* cjson types: */ #define cjson_false (1 << 0) #define cjson_true (1 << 1) #define cjson_null (1 << 2) #define cjson_number (1 << 3) #define cjson_string (1 << 4) #define cjson_array (1 << 5) #define cjson_object (1 << 6)
//删除一个cjson节点, 先删除儿子节点,然后删除自己。对于字符串,需要先释放字符串的内存然后再释放自己的这块内存,对于其他节点,直接释放自己这块内存(目前对这里的儿子节点还是有点不理解,儿子节点(struct cjson类型)到底是什么?干嘛用的?删除的时候为啥要这样做?后边继续分析)
#define cjson_isreference 256
#define cjson_stringisconst 512
/* delete a cjson structure. */ void cjson_delete(cjson *c) { cjson *next; while (c) { next=c->next; if (!(c->type&cjson_isreference) && c->child) cjson_delete(c->child); //先递归删除自己的儿子节点 if (!(c->type&cjson_isreference) && c->valuestring) cjson_free(c->valuestring); if (!(c->type&cjson_stringisconst) && c->string) cjson_free(c->string); cjson_free(c); c=next; } }
//解析数字,源码风格就是这样的,反正我阅读起来相当不方便!建议阅读之前格式还是自己先调整一下!
//解析输入文本生成一个数字,并填充结果项
//传入的参数有两个,这里先只关注num, 返回值是一个字符串
/* parse the input text to generate a number, and populate the result into item. */ static const char *parse_number(cjson *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; if (*num=='-') sign=-1,num++; /* 判断是否为正负数 */ if (*num=='0') num++; /* is zero */ if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); //注意一下这里的语法,两个分号 if (*num=='.' && num[1]>='0' && num[1]<='9') //对小数点后边的部分进行处理 scale记录小数点后边的位数 {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} if (*num=='e' || *num=='e') /* 是否为指数,科学计数法 */ { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++;//判断指数后边幂的正负号 while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');//指数后边10的幂 } //将字符串转换为相应的数值 n=sign*n*pow(10.0,(scale+subscale*signsubscale));/* number = +/- number.fraction * 10^+/- exponent */ item->valuedouble=n;//将算出来的值存入缓存 item->valueint=(int)n;//同上 item->type=cjson_number; //目标类型为数字 return num; }
其实上面的看着稍微有点复杂,但是仔细分析其实还是很简单的!最简单的就是随便写个数然后把自己当计算机一步一步执行上面的代码然后看结果!这里主要是字符串的科学计数法转变为数学上的科学计数法!
继续next one!
static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }吐槽一下,源码中这种编码风格真的很别扭!__pow2gt(x) 函数技巧, 返回 一个比x 的 n (其中n是2的幂),并且是最小的幂!说白了就是将一个数后边所有的位都置1然后再+1,自己可以随便代个数中手算验证一下!这里参数是int型,如果是负数怎么办?这个还没验证过!继续向下看吧!
typedef struct {char *buffer; int length; int offset; } printbuffer; /* ensure 函数 是一个 协助 printbuffer 分配内存的一个函数 * len 表示当前字符串的字符串起始偏移量 即 newbuffer+p->offset 起始的 */ static char* ensure(printbuffer *p,int needed) { char *newbuffer;int newsize; if (!p || !p->buffer) return 0;//传入参数合法性检测 needed+=p->offset;//需要额外分配的内存 也就是偏移量 if (needed<=p->length) return p->buffer+p->offset;//内存够用直接返回 newsize=pow2gt(needed);//不得不说这个用的很巧妙 newbuffer=(char*)cjson_malloc(newsize);//malloc出新内存 用来放什么?后边来看是放buffer里面的内容 if (!newbuffer) {cjson_free(p->buffer);p->length=0,p->buffer=0;return 0;} if (newbuffer) memcpy(newbuffer,p->buffer,p->length);//复制内容 这一行有点不明白意图?为啥要这样做? cjson_free(p->buffer);//释放掉之前的buffer p->length=newsize; p->buffer=newbuffer; return newbuffer+p->offset;//为什么要返回这个?这个函数到底要干吗? }这个函数大概的功能就是协助分配内存,还没摸透具体为什么要这么做,后边边看边理解吧!
static int update(printbuffer *p) { char *str; if (!p || !p->buffer) return 0; str=p->buffer+p->offset; return p->offset+strlen(str); }返回的是一个地址,偏移量加上字符串str长度的地址,但这里char *str只是个局部指针变量!该指针指向原有的buffer+offset地址处后面的字符串,返回值是一个int型!
//先看函数参数、返回值!返回值为一个字符串地址,这里看到sprintf的这种用法就知道是讲数字转换为字符串数组!
//也就是 300转换为“300”
static char *print_number(cjson *item,printbuffer *p) { char *str=0; double d=item->valuedouble;//取出item里面的valuedouble if (d==0) { if (p) str=ensure(p,2);//申请两个字节内存 这个结合pow2gt函数 else str=(char*)cjson_malloc(2); /* special case for 0. */ if (str) strcpy(str,"0");//加一个字符0 ??? } else if (fabs(((double)item->valueint)-d)<=dbl_epsilon && d<=int_max && d>=int_min) { if (p) str=ensure(p,21); else str=(char*)cjson_malloc(21); /* 2^64+1 can be represented in 21 chars. */ if (str) sprintf(str,"%d",item->valueint); } else { if (p) str=ensure(p,64); else str=(char*)cjson_malloc(64); /* this is a nice tradeoff. */ if (str) { if (fpclassify(d) != fp_zero && !isnormal(d)) sprintf(str,"null"); else if (fabs(floor(d)-d)<=dbl_epsilon && fabs(d)<1.0e60) sprintf(str,"%.0f",d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); else sprintf(str,"%f",d); } } return str; }上面的函数中有个地方可以关注一下dbl_epsilon
继续next one!
//将十六进制的字符串转换为数字表示!这个函数自己也可以单独写个小程序测试一下,比较简单明了!
static unsigned parse_hex4(const char *str) { unsigned h=0; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; h=h<<4;str++; if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; return h; }
//输入文本解析成一个因为保有的字符串,并填充项
static const unsigned char firstbytemark[7] = { 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }; static const char *parse_string(cjson *item,const char *str,const char **ep) { const char *ptr=str+1,*end_ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; if (*str!='\"') {*ep=str;return 0;} /* not a string! */ while (*end_ptr!='\"' && *end_ptr && ++len) if (*end_ptr++ == '\\') end_ptr++; /* skip escaped quotes. */ out=(char*)cjson_malloc(len+1); /* this is how long we need for the string, roughly. */ if (!out) return 0; item->valuestring=out; /* assign here so out will be deleted during cjson_delete() later */ item->type=cjson_string; ptr=str+1;ptr2=out; while (ptr < end_ptr) { if (*ptr!='\\') *ptr2++=*ptr++; else { ptr++; switch (*ptr) { case 'b': *ptr2++='\b'; break; case 'f': *ptr2++='\f'; break; case 'n': *ptr2++='\n'; break; case 'r': *ptr2++='\r'; break; case 't': *ptr2++='\t'; break; case 'u': /* transcode utf16 to utf8. */ uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ if (ptr >= end_ptr) {*ep=str;return 0;} /* invalid */ if ((uc>=0xdc00 && uc<=0xdfff) || uc==0) {*ep=str;return 0;} if (uc>=0xd800 && uc<=0xdbff) /* utf16 surrogate pairs. */ { if (ptr+6 > end_ptr) {*ep=str;return 0;} /* invalid */ if (ptr[1]!='\\' || ptr[2]!='u') {*ep=str;return 0;} uc2=parse_hex4(ptr+3);ptr+=6; if (uc2<0xdc00 || uc2>0xdfff) {*ep=str;return 0;} uc=0x10000 + (((uc&0x3ff)<<10) | (uc2&0x3ff)); } len=4; if (uc<0x80) len=1; else if (uc<0x800) len=2; else if (uc<0x10000) len=3; ptr2+=len; switch (len) { case 4: *--ptr2 =((uc | 0x80) & 0xbf); uc >>= 6; case 3: *--ptr2 =((uc | 0x80) & 0xbf); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xbf); uc >>= 6; case 1: *--ptr2 =(uc | firstbytemark[len]); } ptr2+=len; break; default: *ptr2++=*ptr; break; } ptr++; } } *ptr2=0; if (*ptr=='\"') ptr++; return ptr; }函数有点长,理解起来也稍微有点费劲!反正函数的大体功能还有里面的处理流程还没弄明白!先看后边,看的差不多再测试程序!通过运行效果反过来再看程序里面的函数处理,先只能迂回战术了!
//下面的一个函数功能和上面差不多,就是将数据填充成可以打印出来的字符串
static char *print_string_ptr(const char *str,printbuffer *p) { const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; if (!str) { if (p) out=ensure(p,3); else out=(char*)cjson_malloc(3); if (!out) return 0; strcpy(out,"\"\""); return out; } for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; if (!flag) { len=ptr-str; if (p) out=ensure(p,len+3); else out=(char*)cjson_malloc(len+3); if (!out) return 0; ptr2=out;*ptr2++='\"'; strcpy(ptr2,str); ptr2[len]='\"'; ptr2[len+1]=0; return out; } ptr=str; while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} if (p) out=ensure(p,len+3); else out=(char*)cjson_malloc(len+3); if (!out) return 0; ptr2=out;ptr=str; *ptr2++='\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (token=*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ } } } *ptr2++='\"';*ptr2++=0; return out; }
//做了一层封装 以字符的形式填充
static char *print_string(cjson *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}
//跳过空格
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
//解析对象,创建一个新的根并初始化,返回一个cjson类型
cjson *cjson_parsewithopts(const char *value,const char **return_parse_end,int require_null_terminated) { const char *end=0,**ep=return_parse_end?return_parse_end:&global_ep; cjson *c=cjson_new_item(); *ep=0; if (!c) return 0; /* memory fail */ end=parse_value(c,skip(value),ep); if (!end) {cjson_delete(c);return 0;} /* parse failure. ep is set. */ /* if we require null-terminated json without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) {end=skip(end);if (*end) {cjson_delete(c);*ep=end;return 0;}} if (return_parse_end) *return_parse_end=end; return c; }
//二次封装的几个函数
/* default options for cjson_parse */ cjson *cjson_parse(const char *value) {return cjson_parsewithopts(value,0,0);} /* render a cjson item/entity/structure to text. */ char *cjson_print(cjson *item) {return print_value(item,0,1,0);} char *cjson_printunformatted(cjson *item) {return print_value(item,0,0,0);}
//先继续向下看
char *cjson_printbuffered(cjson *item,int prebuffer,int fmt) { printbuffer p; p.buffer=(char*)cjson_malloc(prebuffer); p.length=prebuffer; p.offset=0; return print_value(item,0,fmt,&p); }
//上面还没有说到print_value函数,下面继续!下面这个函数是解析器的核心!代码比较简洁理解也很容易!
static const char *parse_value(cjson *item,const char *value,const char **ep) { if (!value) return 0; /* fail on null. */ if (!strncmp(value,"null",4)) { item->type=cjson_null; return value+4; } if (!strncmp(value,"false",5)) { item->type=cjson_false; return value+5; } if (!strncmp(value,"true",4)) { item->type=cjson_true; item->valueint=1;return value+4; } if (*value=='\"') { return parse_string(item,value,ep); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value,ep); } if (*value=='{') { return parse_object(item,value,ep); } *ep=value;return 0; /* failure. */ }
//解析数组 终于看到了最核心的代码了 下面的代码大多是合法性检测,实际上代码没两行
static const char *parse_array(cjson *item,const char *value,const char **ep) { cjson *child; if (*value!='[') {*ep=value;return 0;} /* not an array! */ item->type=cjson_array; value=skip(value+1); if (*value==']') return value+1; /* empty array. */ item->child=child=cjson_new_item(); if (!item->child) return 0; /* memory fail */ value=skip(parse_value(child,skip(value),ep)); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cjson *new_item; if (!(new_item=cjson_new_item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item;//原来child指针是指向cjson节点的,还有两个指针作为双向链表指针 value=skip(parse_value(child,skip(value+1),ep)); if (!value) return 0; /* memory fail */ } if (*value==']') return value+1; /* end of array */ *ep=value;return 0; /* malformed. */ }
//下面这个函数就比较长了,返回值为一个要out的字符串!关于下面的解析,代码稍微比较长一点,可以对照着后边的测试程序看,然后可以将测试例程塞进这个函数,看看out出的结果,然后还是把自己当计算机,照着代码一步一步执行!等大体吸收了下面编程的精华,然后就可以回过头来反观大局了!
static char *print_array(cjson *item,int depth,int fmt,printbuffer *p) { char **entries; char *out=0,*ptr,*ret;int len=5; cjson *child=item->child; int numentries=0,i=0,fail=0; size_t tmplen=0; /* how many entries in the array? */ while (child) numentries++,child=child->next; /* explicitly handle numentries==0 */ if (!numentries) { if (p) out=ensure(p,3); else out=(char*)cjson_malloc(3); if (out) strcpy(out,"[]"); return out; } if (p) { /* compose the output array. */ i=p->offset; ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; child=item->child; while (child && !fail) { print_value(child,depth+1,fmt,p); p->offset=update(p); if (child->next) { len=fmt?2:1;ptr=ensure(p,len+1); if (!ptr) return 0;*ptr++=','; if(fmt)*ptr++=' ';*ptr=0;p->offset+=len; } child=child->next; } ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; out=(p->buffer)+i; } else { /* allocate an array to hold the values for each */ entries=(char**)cjson_malloc(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); /* retrieve all the results: */ child=item->child; while (child && !fail) { ret=print_value(child,depth+1,fmt,0); entries[i++]=ret; if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } /* if we didn't fail, try to malloc the output string */ if (!fail) out=(char*)cjson_malloc(len); /* if that fails, we fail. */ if (!out) fail=1; /* handle failure. */ if (fail) { for (i=0;i 下面两个函数是解析对象和out一个字符串形式的对象!和上面两个api一样/* build an object from the text. */ static const char *parse_object(cjson *item,const char *value,const char **ep) { cjson *child; if (*value!='{') {*ep=value;return 0;} /* not an object! */ item->type=cjson_object; value=skip(value+1); if (*value=='}') return value+1; /* empty array. */ item->child=child=cjson_new_item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value),ep)); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {*ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1),ep)); /* skip any spacing, get the value. */ if (!value) return 0; while (*value==',') { cjson *new_item; if (!(new_item=cjson_new_item())) return 0; /* memory fail */ child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_string(child,skip(value+1),ep)); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') {*ep=value;return 0;} /* fail! */ value=skip(parse_value(child,skip(value+1),ep)); /* skip any spacing, get the value. */ if (!value) return 0; } if (*value=='}') return value+1; /* end of array */ *ep=value;return 0; /* malformed. */ } /* render an object to text. */ static char *print_object(cjson *item,int depth,int fmt,printbuffer *p) { char **entries=0,**names=0; char *out=0,*ptr,*ret,*str;int len=7,i=0,j; cjson *child=item->child; int numentries=0,fail=0; size_t tmplen=0; /* count the number of entries. */ while (child) numentries++,child=child->next; /* explicitly handle empty object case */ if (!numentries) { if (p) out=ensure(p,fmt?depth+4:3); else out=(char*)cjson_malloc(fmt?depth+4:3); if (!out) return 0; ptr=out;*ptr++='{'; if (fmt) {*ptr++='\n';for (i=0;ioffset; len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; child=item->child;depth++; while (child) { if (fmt) { ptr=ensure(p,depth); if (!ptr) return 0; for (j=0;joffset+=depth; } print_string_ptr(child->string,p); p->offset=update(p); len=fmt?2:1; ptr=ensure(p,len); if (!ptr) return 0; *ptr++=':';if (fmt) *ptr++='\t'; p->offset+=len; print_value(child,depth,fmt,p); p->offset=update(p); len=(fmt?1:0)+(child->next?1:0); ptr=ensure(p,len+1); if (!ptr) return 0; if (child->next) *ptr++=','; if (fmt) *ptr++='\n';*ptr=0; p->offset+=len; child=child->next; } ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; if (fmt) for (i=0;ibuffer)+i; } else { /* allocate space for the names and the objects */ entries=(char**)cjson_malloc(numentries*sizeof(char*)); if (!entries) return 0; names=(char**)cjson_malloc(numentries*sizeof(char*)); if (!names) {cjson_free(entries);return 0;} memset(entries,0,sizeof(char*)*numentries); memset(names,0,sizeof(char*)*numentries); /* collect all the results into our arrays: */ child=item->child;depth++;if (fmt) len+=depth; while (child && !fail) { names[i]=str=print_string_ptr(child->string,0); entries[i++]=ret=print_value(child,depth,fmt,0); if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } /* try to allocate the output string */ if (!fail) out=(char*)cjson_malloc(len); if (!out) fail=1; /* handle failure */ if (fail) { for (i=0;i;i++)>
//官方的源码代码很紧凑,但是很不方便阅读!
/* get array size/item / object item. */ int cjson_getarraysize(cjson *array) {cjson *c=array->child;int i=0;while(c)i++,c=c->next;return i;}//返回节点的个数 cjson *cjson_getarrayitem(cjson *array,int item) {cjson *c=array?array->child:0;while (c && item>0) item--,c=c->next; return c;}//返回第item个节点地址 cjson *cjson_getobjectitem(cjson *object,const char *string)//同上,只是类型不一样 {cjson *c=object?object->child:0;while (c && cjson_strcasecmp(c->string,string)) c=c->next; return c;} int cjson_hasobjectitem(cjson *object,const char *string)//二次封装,暂时还没明白这样做的优势 {return cjson_getobjectitem(object,string)?1:0;} /* utility for array list handling. */ static void suffix_object(cjson *prev,cjson *item) {prev->next=item;item->prev=prev;}//个人理解在链表尾插入一个节点 /* utility for handling references. */ static cjson *create_reference(cjson *item) { cjson *ref=cjson_new_item(); if (!ref) return 0; memcpy(ref,item,sizeof(cjson)); ref->string=0; ref->type|=cjson_isreference;// 与256相与 1 0000 0000(256的二进制表示)这里暂时有点不明白 ref->next=ref->prev=0;//都置空 return ref; } /* add item to array/object. */ void cjson_additemtoarray(cjson *array, cjson *item)//将item节点插入array链表 { cjson *c=array->child; if (!item) return; if (!c) {array->child=item;} //如果为空链表 直接插入 else {while (c && c->next) c=c->next; suffix_object(c,item);} } //将字符串添加进对象 void cjson_additemtoobject(cjson *object,const char *string,cjson *item) { if (!item) return; if (item->string) cjson_free(item->string); item->string=cjson_strdup(string); cjson_additemtoarray(object,item); } void cjson_additemtoobjectcs(cjson *object,const char *string,cjson *item) { if (!item) return; if (!(item->type&cjson_stringisconst) && item->string) cjson_free(item->string); item->string=(char*)string; item->type|=cjson_stringisconst;//512 (10 0000 0000b) cjson_additemtoarray(object,item); }//大多都是二次封装
void cjson_additemreferencetoarray(cjson *array, cjson *item) { cjson_additemtoarray(array,create_reference(item)); } void cjson_additemreferencetoobject(cjson *object,const char *string,cjson *item) { cjson_additemtoobject(object,string,create_reference(item)); } cjson *cjson_detachitemfromarray(cjson *array,int which)//分离链表中第which位置的节点并返回 { cjson *c=array->child; while (c && which>0) c=c->next,which--;//c指向第which个节点 if (!c) return 0; if (c->prev) c->prev->next=c->next; if (c->next) c->next->prev=c->prev; if (c==array->child) array->child=c->next; c->prev=c->next=0; return c; } void cjson_deleteitemfromarray(cjson *array,int which) { cjson_delete(cjson_detachitemfromarray(array,which)); } //功能同上差不多 只是类型不同 cjson *cjson_detachitemfromobject(cjson *object,const char *string) { int i=0; cjson *c=object->child; while (c && cjson_strcasecmp(c->string,string)) i++,c=c->next; if (c) return cjson_detachitemfromarray(object,i); return 0; } void cjson_deleteitemfromobject(cjson *object,const char *string) { cjson_delete(cjson_detachitemfromobject(object,string)); }
//下面的api也是核心部分,要好好分析吸收
/* replace array/object items with new ones. */ void cjson_insertiteminarray(cjson *array,int which,cjson *newitem)//在链表中插入一个新的节点 { cjson *c=array->child; while (c && which>0) c=c->next,which--;//先定位到替换的位置 if (!c) {cjson_additemtoarray(array,newitem);return;} newitem->next=c; newitem->prev=c->prev; c->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem; } void cjson_replaceiteminarray(cjson *array,int which,cjson *newitem)//用新的节点替换原有的某一个节点 { cjson *c=array->child; while (c && which>0) c=c->next,which--; if (!c) return; newitem->next=c->next; newitem->prev=c->prev; if (newitem->next) newitem->next->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem; c->next=c->prev=0; cjson_delete(c); } void cjson_replaceiteminobject(cjson *object,const char *string,cjson *newitem)//同上,只是换个类型 { int i=0; cjson *c=object->child; while(c && cjson_strcasecmp(c->string,string)) i++,c=c->next; if(c) { newitem->string=cjson_strdup(string); cjson_replaceiteminarray(object,i,newitem); } }1.重要函数说明
【1】两个创建
【创建json对象】cjson *cjson_createobject(void);
【创建json数组】cjson *cjson_createarray(void);
【2】两种添加
【向对象中添加】voidcjson_additemtoobject(cjson *object,const char *string,cjson *item);
【向数组中添加】void cjson_additemtoarray(cjson *array, cjson *item);
【3】常用几招
【向对象中增加数字】cjson_additemtoobject(root, "value", cjson_createnumber(value));
【向对象中增加文件】cjson_additemtoobject(root, "string", cjson_createstring(string));
【4】json嵌套
【向对象中增加数组】cjson_additemtoobject(root, "rows", rows = cjson_createarray());
【向数组中增加对象】cjson_additemtoarray(rows, row = cjson_createobject());
【简单说明】
【1】cjson_additemtoobject(root, "value", cjson_createnumber(value));
【2】cjson_addnumbertoobject(root, "value", value);
【1】和【2】效果完全相同。
【简单说明】
【1】 cjson_additemtoobject(root, "name", cjson_createstring(name));
【2】 cjson_addstringtoobject(root, "name",name);【1】和【2】效果完全相同。
cjson的代码风格!代码很紧凑,但是不是很方便阅读!
//源码的格式稍微调整了了一下
/* create basic types: */ cjson *cjson_createnull(void) { cjson *item=cjson_new_item(); if(item) item->type=cjson_null; return item; } cjson *cjson_createtrue(void) { cjson *item=cjson_new_item(); if(item) item->type=cjson_true; return item; } cjson *cjson_createfalse(void) { cjson *item=cjson_new_item(); if(item) item->type=cjson_false; return item; } cjson *cjson_createbool(int b) { cjson *item=cjson_new_item(); if(item) item->type=b?cjson_true:cjson_false; return item; } cjson *cjson_createnumber(double num) { cjson *item=cjson_new_item(); if(item) { item->type=cjson_number; item->valuedouble=num; item->valueint=(int)num; } return item; } cjson *cjson_createstring(const char *string) { cjson *item=cjson_new_item(); if(item) { item->type=cjson_string; item->valuestring=cjson_strdup(string); if(!item->valuestring) { cjson_delete(item); return 0; } } return item; } cjson *cjson_createarray(void) { cjson *item=cjson_new_item(); if(item) item->type=cjson_array; return item; } cjson *cjson_createobject(void) { cjson *item=cjson_new_item(); if(item) item->type=cjson_object; return item; }上的api函数笔记简单!这里就不啰嗦了!
//下面的四个api都差不多,只是创建不同类型的数组
/* create arrays: */ cjson *cjson_createintarray(const int *numbers,int count) { int i;cjson *n=0,*p=0,*a=cjson_createarray(); for(i=0;a && ichild=n; else suffix_object(p,n);//插入节点 p=n; } return a; } cjson *cjson_createfloatarray(const float *numbers,int count) { int i;cjson *n=0,*p=0,*a=cjson_createarray(); for(i=0;a && ichild=n; else suffix_object(p,n); p=n; } return a; } cjson *cjson_createdoublearray(const double *numbers,int count) { int i;cjson *n=0,*p=0,*a=cjson_createarray(); for(i=0;a && ichild=n; else suffix_object(p,n); p=n; } return a; } cjson *cjson_createstringarray(const char **strings,int count) { int i;cjson *n=0,*p=0,*a=cjson_createarray(); for(i=0;a && ichild=n; else suffix_object(p,n); p=n; } return a; }
//最后两个api
/* duplication */ cjson *cjson_duplicate(cjson *item,int recurse)//拷贝副本 是否递归拷贝 { cjson *newitem,*cptr,*nptr=0,*newchild; if (!item) return 0; /* create new item */ newitem=cjson_new_item(); if (!newitem) return 0; /* copy over all vars */ newitem->type=item->type&(~cjson_isreference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; if (item->valuestring) { newitem->valuestring=cjson_strdup(item->valuestring); if (!newitem->valuestring) {cjson_delete(newitem);return 0;} } if (item->string) { newitem->string=cjson_strdup(item->string); if (!newitem->string) {cjson_delete(newitem);return 0;} } /* if non-recursive, then we're done! */ if (!recurse) return newitem; /* walk the ->next chain for the child. */ cptr=item->child; while (cptr) { newchild=cjson_duplicate(cptr,1); if (!newchild) {cjson_delete(newitem);return 0;} if (nptr) { nptr->next=newchild,newchild->prev=nptr; nptr=newchild; } else {newitem->child=newchild;nptr=newchild;} cptr=cptr->next; } return newitem; } void cjson_minify(char *json) { char *into=json; while (*json) { if (*json==' ') json++; else if (*json=='\t') json++; /* whitespace characters. */ else if (*json=='\r') json++; else if (*json=='\n') json++; else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} else *into++=*json++; /* all other characters. */ } *into=0; /* and null-terminate. */ }
cjson.h
#ifndef cjson__h #define cjson__h #ifdef __cplusplus extern "c" { #endif /* cjson types: */ #define cjson_false (1 << 0) #define cjson_true (1 << 1) #define cjson_null (1 << 2) #define cjson_number (1 << 3) #define cjson_string (1 << 4) #define cjson_array (1 << 5) #define cjson_object (1 << 6) #define cjson_isreference 256 #define cjson_stringisconst 512 /* the cjson structure: */ typedef struct cjson { /* next/prev allow you to walk array/object chains. alternatively, use getarraysize/getarrayitem/getobjectitem */ struct cjson *next,*prev; /* an array or object item will have a child pointer pointing to a chain of the items in the array/object. */ struct cjson *child; int type; /* the type of the item, as above. */ char *valuestring; /* the item's string, if type==cjson_string */ int valueint; /* the item's number, if type==cjson_number */ double valuedouble; /* the item's number, if type==cjson_number */ /* the item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; } cjson; typedef struct cjson_hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cjson_hooks; /* supply malloc, realloc and free functions to cjson */ extern void cjson_inithooks(cjson_hooks* hooks); extern cjson *cjson_parse(const char *value); /* render a cjson entity to text for transfer/storage. free the char* when finished. */ extern char *cjson_print(cjson *item); extern char *cjson_printunformatted(cjson *item); /* render a cjson entity to text using a buffered strategy. prebuffer is a guess at the final size. *guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ extern char *cjson_printbuffered(cjson *item,int prebuffer,int fmt); /* delete a cjson entity and all subentities. */ extern void cjson_delete(cjson *c); /* returns the number of items in an array (or object). */ extern int cjson_getarraysize(cjson *array); /* retrieve item number "item" from array "array". returns null if unsuccessful. */ extern cjson *cjson_getarrayitem(cjson *array,int item); /* get item "string" from object. case insensitive. */ extern cjson *cjson_getobjectitem(cjson *object,const char *string); extern int cjson_hasobjectitem(cjson *object,const char *string); /* for analysing failed parses. this returns a pointer to the parse error. * you'll probably need to look a few chars back to make sense of it. defined when cjson_parse() returns 0. 0 when cjson_parse() succeeds. */ extern const char *cjson_geterrorptr(void); /* these calls create a cjson item of the appropriate type. */ extern cjson *cjson_createnull(void); extern cjson *cjson_createtrue(void); extern cjson *cjson_createfalse(void); extern cjson *cjson_createbool(int b); extern cjson *cjson_createnumber(double num); extern cjson *cjson_createstring(const char *string); extern cjson *cjson_createarray(void); extern cjson *cjson_createobject(void); /* these utilities create an array of count items. */ extern cjson *cjson_createintarray(const int *numbers,int count); extern cjson *cjson_createfloatarray(const float *numbers,int count); extern cjson *cjson_createdoublearray(const double *numbers,int count); extern cjson *cjson_createstringarray(const char **strings,int count); /* append item to the specified array/object. */ extern void cjson_additemtoarray(cjson *array, cjson *item); extern void cjson_additemtoobject(cjson *object,const char *string,cjson *item); extern void cjson_additemtoobjectcs(cjson *object,const char *string,cjson *item); /* append reference to item to the specified array/object. use this when you want to * add an existing cjson to a new cjson, but don't want to corrupt your existing cjson. */ extern void cjson_additemreferencetoarray(cjson *array, cjson *item); extern void cjson_additemreferencetoobject(cjson *object,const char *string,cjson *item); /* remove/detatch items from arrays/objects. */ extern cjson *cjson_detachitemfromarray(cjson *array,int which); extern void cjson_deleteitemfromarray(cjson *array,int which); extern cjson *cjson_detachitemfromobject(cjson *object,const char *string); extern void cjson_deleteitemfromobject(cjson *object,const char *string); /* update array items. */ extern void cjson_insertiteminarray(cjson *array,int which,cjson *newitem); extern void cjson_replaceiteminarray(cjson *array,int which,cjson *newitem); extern void cjson_replaceiteminobject(cjson *object,const char *string,cjson *newitem); /* duplicate a cjson item */ extern cjson *cjson_duplicate(cjson *item,int recurse); /* duplicate will create a new, identical cjson item to the one you pass, in new memory that will *need to be released. with recurse!=0, it will duplicate any children connected to the item. *the item->next and ->prev pointers are always zero on return from duplicate. */ /* parsewithopts allows you to require (and check) that the json is null terminated, *and to retrieve the pointer to the final byte parsed. */ extern cjson *cjson_parsewithopts(const char *value,const char **return_parse_end,int require_null_terminated); extern void cjson_minify(char *json);
makefile (这个makefile 可以将目标文件编译出动态库,静态库,自带的测试程序编译成目标文件,对于我这样的小白来说还是很值得学习研究的)
obj = cjson.o libname = libcjson tests = test prefix ?= /usr/local include_path ?= include/cjson library_path ?= lib install_include_path = $(destdir)$(prefix)/$(include_path) install_library_path = $(destdir)$(prefix)/$(library_path) install ?= cp -a r_cflags = -fpic $(cflags) -wall -werror -wstrict-prototypes -wwrite-strings -d_posix_c_source=200112l uname_s := $(shell sh -c 'uname -s 2>/dev/null || echo false') ## shared lib dylibname = $(libname).so dylibcmd = $(cc) -shared -o $(dylibname) ## create dynamic (shared) library on darwin (base os for macosx and ios) ifeq (darwin, $(uname_s)) dylibname = $(libname).dylib ## create dyanmic (shared) library on sunos else ifeq (sunos, $(uname_s)) dylibcmd = $(cc) -g -o $(dylibname) install = cp -r endif ## static lib stlibname = $(libname).a .phony: all clean install all: $(dylibname) $(stlibname) $(tests) $(dylibname): $(obj) $(dylibcmd) $< $(ldflags) $(stlibname): $(obj) ar rcs $@ $< $(obj): cjson.c cjson.h .c.o: $(cc) -ansi -pedantic -c $(r_cflags) $< $(tests): cjson.c cjson.h test.c $(cc) cjson.c test.c -o test -lm -i. install: $(dylibname) $(stlibname) mkdir -p $(install_library_path) $(install_include_path) $(install) cjson.h $(install_include_path) $(install) $(dylibname) $(install_library_path) $(install) $(stlibname) $(install_library_path) uninstall: rm -rf $(install_library_path)/$(dylibname) rm -rf $(install_library_path)/$(stlibname) rm -rf $(install_include_path)/cjson.h clean: rm -rf $(dylibname) $(stlibname) $(tests) *.o
test.c
#include #include #include "cjson.h" /* parse text to json, then render back to text, and print! */ void doit(char *text) { char *out;cjson *json; json=cjson_parse(text); if (!json) {printf("error before: [%s]\n",cjson_geterrorptr());} else { out=cjson_print(json); cjson_delete(json); printf("%s\n",out); free(out); } } /* read a file, parse, render back, etc. */ void dofile(char *filename) { file *f;long len;char *data; f=fopen(filename,"rb");fseek(f,0,seek_end);len=ftell(f);fseek(f,0,seek_set); data=(char*)malloc(len+1);fread(data,1,len,f);data[len]='\0';fclose(f); doit(data); free(data); } /* used by some code below as an example datatype. */ struct record {const char *precision;double lat,lon;const char *address,*city,*state,*zip,*country; }; /* create a bunch of objects as demonstration. */ void create_objects() { cjson *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. */ /* our "days of the week" array: */ const char *strings[7]={"sunday","monday","tuesday","wednesday","thursday","friday","saturday"}; /* our matrix: */ int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}}; /* our "gallery" item: */ int ids[4]={116,943,234,38793}; /* our array of "records": */ struct record fields[2]={ {"zip",37.7668,-1.223959e+2,"","san francisco","ca","94107","us"}, {"zip",37.371991,-1.22026e+2,"","sunnyvale","ca","94085","us"}}; /* here we construct some json standards, from the json site. */ /* our "video" datatype: */ root=cjson_createobject(); cjson_additemtoobject(root, "name", cjson_createstring("jack (\"bee\") nimble")); cjson_additemtoobject(root, "format", fmt=cjson_createobject()); cjson_addstringtoobject(fmt,"type", "rect"); cjson_addnumbertoobject(fmt,"width", 1920); cjson_addnumbertoobject(fmt,"height", 1080); cjson_addfalsetoobject (fmt,"interlace"); cjson_addnumbertoobject(fmt,"frame rate", 24); out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); /* print to text, delete the cjson, print it, release the string. */ /* our "days of the week" array: */ root=cjson_createstringarray(strings,7); out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); /* our matrix: */ root=cjson_createarray(); for (i=0;i<3;i++) cjson_additemtoarray(root,cjson_createintarray(numbers[i],3)); /* cjson_replaceiteminarray(root,1,cjson_createstring("replacement")); */ out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); /* our "gallery" item: */ root=cjson_createobject(); cjson_additemtoobject(root, "image", img=cjson_createobject()); cjson_addnumbertoobject(img,"width",800); cjson_addnumbertoobject(img,"height",600); cjson_addstringtoobject(img,"title","view from 15th floor"); cjson_additemtoobject(img, "thumbnail", thm=cjson_createobject()); cjson_addstringtoobject(thm, "url", "http:/*www.example.com/image/481989943"); cjson_addnumbertoobject(thm,"height",125); cjson_addstringtoobject(thm,"width","100"); cjson_additemtoobject(img,"ids", cjson_createintarray(ids,4)); out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); /* our array of "records": */ root=cjson_createarray(); for (i=0;i<2;i++) { cjson_additemtoarray(root,fld=cjson_createobject()); cjson_addstringtoobject(fld, "precision", fields[i].precision); cjson_addnumbertoobject(fld, "latitude", fields[i].lat); cjson_addnumbertoobject(fld, "longitude", fields[i].lon); cjson_addstringtoobject(fld, "address", fields[i].address); cjson_addstringtoobject(fld, "city", fields[i].city); cjson_addstringtoobject(fld, "state", fields[i].state); cjson_addstringtoobject(fld, "zip", fields[i].zip); cjson_addstringtoobject(fld, "country", fields[i].country); } /* cjson_replaceiteminobject(cjson_getarrayitem(root,1),"city",cjson_createintarray(ids,4)); */ out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); root=cjson_createobject(); cjson_addnumbertoobject(root,"number", 1.0/0.0); out=cjson_print(root); cjson_delete(root); printf("%s\n",out); free(out); } int main (int argc, const char * argv[]) { /* a bunch of json: */ char text1[]="{\n\"name\": \"jack (\\\"bee\\\") nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}"; char text2[]="[\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"]"; char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n"; char text4[]="{\n \"image\": {\n \"width\": 800,\n \"height\": 600,\n \"title\": \"view from 15th floor\",\n \"thumbnail\": {\n \"url\": \"http:/*www.example.com/image/481989943\",\n \"height\": 125,\n \"width\": \"100\"\n },\n \"ids\": [116, 943, 234, 38793]\n }\n }"; char text5[]="[\n {\n \"precision\": \"zip\",\n \"latitude\": 37.7668,\n \"longitude\": -122.3959,\n \"address\": \"\",\n \"city\": \"san francisco\",\n \"state\": \"ca\",\n \"zip\": \"94107\",\n \"country\": \"us\"\n },\n {\n \"precision\": \"zip\",\n \"latitude\": 37.371991,\n \"longitude\": -122.026020,\n \"address\": \"\",\n \"city\": \"sunnyvale\",\n \"state\": \"ca\",\n \"zip\": \"94085\",\n \"country\": \"us\"\n }\n ]"; char text6[] = "" "\n" "\n" "\n" "\n" " html, body, iframe { margin: 0; padding: 0; height: 100%; }\n" " iframe { display: block; width: 100%; border: none; }\n" "\n" "\n" "\n" "\n" "<iframe src="//s3.amazonaws.com/heroku_pages/error.html">\n" " <p>application error</p>\n" "</iframe>\n" "\n" "\n"; /* process each json textblock by parsing, then rebuilding: */ doit(text1); doit(text2); doit(text3); doit(text4); doit(text5); doit(text6); /* parse standard testfiles: */ /* dofile("../../tests/test1"); */ /* dofile("../../tests/test2"); */ /* dofile("../../tests/test3"); */ /* dofile("../../tests/test4"); */ /* dofile("../../tests/test5"); */ /* dofile("../../tests/test6"); */ /* now some samplecode for building objects concisely: */ create_objects(); return 0; }编译运行效果部分截图:
上面的test.c中也可以一个一个的测试,针对测试用例还有运行的结果再反过来分析源码!这样理解起来也会更容易一点!其中下面是doit函数调用的几个函数关系!;i++)>;i++)>;j++)>;i++)>
上一篇: 获取URL文件名后缀
下一篇: Flash AS3教程:下雪动画效果