程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C json實戰引擎 二 , 實現構造部分,json實戰

C json實戰引擎 二 , 實現構造部分,json實戰

編輯:關於C語言

C json實戰引擎 二 , 實現構造部分,json實戰


引言

  這篇博文和前一篇 C json實戰引擎一,實現解析部分設計是相同的,都是采用遞歸下降分析.

這裡扯一點 假如你是學生 推薦一本書 給 大家

  自制編程語言 http://baike.baidu.com/link?url=jIFOBNt26ykhnPr2-UaaDc5_I7gZURXdJ15P2iBXwbglXkdz2qT_tqAz4KoFF0rsS2IQbIP-ij2Ar5EMRzMcuq

當然學了上面內容,以後對編譯鏈接設計方面會有很大提高. 但是對於 其它 也沒有什麼鳥用.

再扯一點 如果想流暢的看完並成功寫完上面書中三個案例. 你還需要 看完 市面上關於 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 = 0;
        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的冪,並且是最小的冪.是一種技巧記住就可以了.估計都是那些寫匯編的老代碼遺留下來的潛規則吧.

性能沒的說. 不明白就當有個印象.

 1 // 2^n>=x , n是最小的整數
 2 static int __pow2gt(int x)
 3 {
 4     --x;
 5     x |= x >> 1;
 6     x |= x >> 2;
 7     x |= x >> 4;
 8     x |= x >> 8;
 9     x |= x >> 16;
10     return x + 1;
11 }

到這裡 這幾個函數 就可以代表這整篇文章了. 後面就可以省略了.

 

正文

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 = 0;

    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) + 1); // 體積變小 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, 6))) strcpy(out, "false"); break;
    case _CJSON_TRUE: if ((out = __ensure(p, 5))) strcpy(out, "true"); break;
    case _CJSON_NULL: if ((out = __ensure(p, 5))) 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 處理函數, 也很直白

 1 // 同樣 假定 item 和 p都是存在且不為NULL, 相信這些代碼是安全的
 2 static char* __print_object(cjson_t item, tstring p)
 3 {
 4     char* ptr;
 5     int i, ncut, len;
 6     cjson_t child = item->child;
 7 
 8     // 得到孩子結點的深度
 9     for (ncut = 0; child; child = child->child)
10         ++ncut;
11     if (!ncut) {
12         char* out = NULL;
13         if (!(out = __ensure(p, 3)))
14             strcpy(out, "{}");
15         return out;
16     }
17 
18     i = p->len;
19     if (!(ptr = __ensure(p, 2)))
20         return NULL;
21     *ptr++ = '{';
22     *ptr -= '\0';
23     p->len += 1;
24     // 根據子結點 處理
25     for (child = item->child; (child); child = child->next) {
26         __print_string(child->key, p);
27         p->len = __update(p);
28 
29         //加入一個冒號
30         if (!(ptr = __ensure(p, 1)))
31             return NULL;
32         *ptr++ = ':';
33         p->len += 1;
34 
35         //繼續打印一個值
36         __print_value(child, p);
37         p->len = __update(p);
38 
39         //結算最後內容
40         len = child->next ? 1 : 0;
41         if ((ptr = __ensure(p, len + 1)) == NULL)
42             return NULL;
43         if (child->next)
44             *ptr++ = ',';
45         *ptr = '\0';
46         p->len += len;
47     }
48     if (!(ptr = __ensure(p, 2)))
49         return NULL;
50     *ptr++ = '}';
51     *ptr = '\0';
52     return p->str + i;
53 }

先處理key ,後面value 用 __print_value 處理. 到這裡 基本思路都有了,其它是靠你自己努力臨摹 把鍵盤敲爛!

完整部分代碼如下

 cjson.h / 有些輔助接口沒有實現,下一個博文中全部實現

1 #ifndef _H_CJSON 2 #define _H_CJSON 3 4 // json 中幾種數據類型定義 , 對於C而言 最難的是看不見源碼,而不是api復雜, 更不是業務復雜 5 #define _CJSON_FALSE (0) 6 #define _CJSON_TRUE (1) 7 #define _CJSON_NULL (2) 8 #define _CJSON_NUMBER (3) 9 #define _CJSON_STRING (4) 10 #define _CJSON_ARRAY (5) 11 #define _CJSON_OBJECT (6) 12 13 #define _CJSON_ISREF (256) //set 時候用如果是引用就不釋放了 14 #define _CJSON_ISCONST (512) //set時候用, 如果是const char* 就不釋放了 15 16 struct cjson { 17 struct cjson *next, *prev; 18 struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那麼 child 就不為空 19 20 int type; 21 char *key; // json內容那塊的 key名稱 22 char *vs; // type == _CJSON_STRING, 是一個字符串 23 double vd; // type == _CJSON_NUMBER, 是一個num值, ((int)c->vd) 轉成int 或 bool 24 }; 25 26 //定義cjson_t json類型 27 typedef struct cjson* cjson_t; 28 29 /* 30 * 這個宏,協助我們得到 int 值 或 bool 值 31 * 32 * item : 待處理的目標cjson_t結點 33 */ 34 #define cjson_getint(item) \ 35 ((int)((item)->vd)) 36 37 /* 38 * 刪除json串內容 39 * c : 待釋放json_t串內容 40 */ 41 extern void cjson_delete(cjson_t* pc); 42 43 /* 44 * 對json字符串解析返回解析後的結果 45 * jstr : 待解析的字符串 46 */ 47 extern cjson_t cjson_parse(const char* jstr); 48 49 /* 50 * 根據 item當前結點的 next 一直尋找到 NULL, 返回個數 51 *推薦是數組使用 52 * array : 待處理的cjson_t數組對象 53 * : 返回這個數組中長度 54 */ 55 extern int cjson_getlen(cjson_t array); 56 57 /* 58 * 根據索引得到這個數組中對象 59 * array : 數組對象 60 * idx : 查找的索引 必須 [0,cjson_getlen(array)) 范圍內 61 * : 返回查找到的當前對象 62 */ 63 extern cjson_t cjson_getarray(cjson_t array, int idx); 64 65 /* 66 * 根據key得到這個對象 相應位置的值 67 * object : 待處理對象中值 68 * key : 尋找的key 69 * : 返回 查找 cjson_t 對象 70 */ 71 extern cjson_t cjson_getobject(cjson_t object, const char* key); 72 73 74 // --------------------------------- 下面是 cjson 輸出部分的處理代碼 ----------------------------------------- 75 76 /* 77 * 這裡是將 cjson_t item 轉換成字符串內容,需要自己free 78 * item : cjson的具體結點 79 * : 返回生成的item的json串內容 80 */ 81 extern char* cjson_print(cjson_t item); 82 83 // --------------------------------- 下面是 cjson 輸出部分的輔助代碼 ----------------------------------------- 84 85 /* 86 * 創建一個bool的對象 b==0表示false,否則都是true 87 * b : bool 值 最好是 _Bool 88 * : 返回 創建好的json 內容 89 */ 90 extern cjson_t cjson_newbool(int b); 91 extern cjson_t cjson_newnumber(double vd); 92 extern cjson_t cjson_newstring(const char* vs); 93 extern cjson_t cjson_newarray(void); 94 extern cjson_t cjson_newobject(void); 95 96 /* 97 * 按照類型,創建 對映類型的數組 cjson對象 98 *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING 99 * type : 類型目前支持 上面幾種類型 100 * array : 數組原始數據 101 * len : 數組中元素長度 102 * : 返回創建的數組對象 103 */ 104 extern cjson_t cjson_newtypearray(int type, const void* array, int len); 105 106 /* 107 * 將 jstr中 不需要解析的字符串都去掉 108 * jstr : 待處理的json串 109 * : 返回壓縮後的json串內容 110 */ 111 extern char* cjson_mini(char* jstr); 112 113 /* 114 * 將json文件解析成json內容返回 115 * jpath : json串路徑 116 * : 返回處理好的cjson_t 內容,失敗返回NULL 117 */ 118 extern cjson_t cjson_dofile(char* jpath); 119 120 #endif // !_H_CJSON View Code

cjson.c

1 #include <cjson.h> 2 #include <schead.h> 3 #include <sclog.h> 4 #include <tstring.h> 5 #include <float.h> 6 #include <math.h> 7 8 // 刪除cjson 9 static void __cjson_delete(cjson_t c) 10 { 11 cjson_t next; 12 while (c) { 13 next = c->next; 14 //遞歸刪除兒子 15 if (!(c->type & _CJSON_ISREF)) { 16 if (c->child) //如果不是尾遞歸,那就先遞歸 17 __cjson_delete(c->child); 18 if (c->vs) 19 free(c->vs); 20 } 21 else if (!(c->type & _CJSON_ISCONST) && c->key) 22 free(c->key); 23 free(c); 24 c = next; 25 } 26 } 27 28 /* 29 * 刪除json串內容,最近老是受清華的老學生打擊, 會起來的...... 30 * c : 待釋放json_t串內容 31 */ 32 void 33 cjson_delete(cjson_t* pc) 34 { 35 if (!pc || !*pc) 36 return; 37 __cjson_delete(*pc); 38 *pc = NULL; 39 } 40 41 //構造一個空 cjson 對象 42 static inline cjson_t __cjson_new(void) 43 { 44 cjson_t c = calloc(1, sizeof(struct cjson)); 45 if (!c) { 46 SL_FATAL("calloc sizeof struct cjson error!"); 47 exit(_RT_EM); 48 } 49 return c; 50 } 51 52 // 簡化的代碼段,用宏來簡化代碼書寫 , 16進制處理 53 #define __parse_hex4_code(c, h) \ 54 if (c >= '0' && c <= '9') \ 55 h += c - '0'; \ 56 else if (c >= 'A' && c <= 'F') \ 57 h += 10 + c - 'A'; \ 58 else if (c >= 'a' && c <= 'z') \ 59 h += 10 + c - 'F'; \ 60 else \ 61 return 0 62 63 // 等到unicode char代碼 64 static unsigned __parse_hex4(const char* str) 65 { 66 unsigned h = 0; 67 char c = *str; 68 //第一輪 69 __parse_hex4_code(c, h); 70 h <<= 4; 71 c = *++str; 72 //第二輪 73 __parse_hex4_code(c, h); 74 h <<= 4; 75 c = *++str; 76 //第三輪 77 __parse_hex4_code(c, h); 78 h <<= 4; 79 c = *++str; 80 //第四輪 81 __parse_hex4_code(c, h); 82 83 return h; 84 } 85 86 // 分析字符串的子函數, 87 static const char* __parse_string(cjson_t item, const char* str) 88 { 89 static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 90 const char *ptr; 91 char *nptr, *out; 92 int len; 93 char c; 94 unsigned uc, nuc; 95 96 if (*str != '\"') { // 檢查是否是字符串內容 97 SL_WARNING("need \\\" str => %s error!", str); 98 return NULL; 99 } 100 101 for (ptr = str + 1, len = 0; (c = *ptr++) != '\"' && c; ++len) 102 if (c == '\\') //跳過轉義字符 103 ++ptr; 104 if (!(out = malloc(len + 1))) { 105 SL_FATAL("malloc %d size error!", len + 1); 106 return NULL; 107 } 108 // 這裡復制拷貝內容 109 for (ptr = str + 1, nptr = out; (c = *ptr) != '\"' && c; ++ptr) { 110 if (c != '\\') { 111 *nptr++ = c; 112 continue; 113 } 114 // 處理轉義字符 115 switch ((c = *++ptr)) { 116 case 'b': *nptr++ = '\b'; break; 117 case 'f': *nptr++ = '\f'; break; 118 case 'n': *nptr++ = '\n'; break; 119 case 'r': *nptr++ = '\r'; break; 120 case 't': *nptr++ = '\t'; break; 121 case 'u': // 將utf16 => utf8, 專門的utf處理代碼 122 uc = __parse_hex4(ptr + 1); 123 ptr += 4;//跳過後面四個字符, unicode 124 if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */ 125 126 if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ 127 { 128 if (ptr[1] != '\\' || ptr[2] != 'u') 129 break; /* missing second-half of surrogate. */ 130 nuc = __parse_hex4(ptr + 3); 131 ptr += 6; 132 if (nuc < 0xDC00 || nuc>0xDFFF) 133 break; /* invalid second-half of surrogate. */ 134 uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF)); 135 } 136 137 len = 4; 138 if (uc < 0x80) 139 len = 1; 140 else if (uc < 0x800) 141 len = 2; 142 else if (uc < 0x10000) 143 len = 3; 144 nptr += len; 145 146 switch (len) { 147 case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6; 148 case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6; 149 case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6; 150 case 1: *--nptr = (uc | __marks[len]); 151 } 152 nptr += len; 153 break; 154 default: *nptr++ = c; 155 } 156 } 157 158 *nptr = '\0'; 159 if (c == '\"') 160 ++ptr; 161 item->vs = out; 162 item->type = _CJSON_STRING; 163 return ptr; 164 } 165 166 // 分析數值的子函數,寫的可以 167 static const char* __parse_number(cjson_t item, const char* str) 168 { 169 double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示開始正負, 負為-1, nd 表示小數後面位數 170 int e = 0, es = 1; //e表示後面指數, es表示 指數的正負,負為-1 171 char c; 172 173 if ((c = *str) == '-' || c == '+') { 174 ns = c == '-' ? -1.0 : 1.0; //正負號檢測, 1表示負數 175 ++str; 176 } 177 //處理整數部分 178 for (c = *str; c >= '0' && c <= '9'; c = *++str) 179 n = n * 10 + c - '0'; 180 if (c == '.') 181 for (; (c = *++str) >= '0' && c <= '9'; --nd) 182 n = n * 10 + c - '0'; 183 184 // 處理科學計數法 185 if (c == 'e' || c == 'E') { 186 if ((c = *++str) == '+') //處理指數部分 187 ++str; 188 else if (c == '-') 189 es = -1, ++str; 190 for (; (c = *str) >= '0' && c <= '9'; ++str) 191 e = e * 10 + c - '0'; 192 } 193 194 //返回最終結果 number = +/- number.fraction * 10^+/- exponent 195 n = ns * n * pow(10.0, nd + es * e); 196 item->vd = n; 197 item->type = _CJSON_NUMBER; 198 return str; 199 } 200 201 // 跳過不需要處理的字符 202 static const char* __skip(const char* in) 203 { 204 if (in && *in && *in <= 32) { 205 unsigned char c; 206 while ((c = *++in) && c <= 32) 207 ; 208 } 209 return in; 210 } 211 212 // 遞歸下降分析 需要聲明這些函數 213 static const char* __parse_array(cjson_t item, const char* str); 214 static const char* __parse_object(cjson_t item, const char* str); 215 static const char* __parse_value(cjson_t item, const char* value); 216 217 // 分析數組的子函數, 采用遞歸下降分析 218 static const char* __parse_array(cjson_t item, const char* str) 219 { 220 cjson_t child; 221 if (*str != '[') { 222 SL_WARNING("array str error start: %s.", str); 223 return NULL; 224 } 225 226 item->type = _CJSON_ARRAY; 227 str = __skip(str + 1); 228 if (*str == ']') // 低估提前結束 229 return str + 1; 230 231 item->child = child = __cjson_new(); 232 str = __skip(__parse_value(child, str)); 233 if (!str) {//解析失敗 直接返回 234 SL_WARNING("array str error e n d one: %s.", str); 235 return NULL; 236 } 237 while (*str == ',') { 238 cjson_t nitem = __cjson_new(); 239 child->next = nitem; 240 nitem->prev = child; 241 child = nitem; 242 str = __skip(__parse_value(child, __skip(str + 1))); 243 if (!str) {// 寫代碼是一件很爽的事 244 SL_WARNING("array str error e n d two: %s.", str); 245 return NULL; 246 } 247 } 248 249 if (*str != ']') { 250 SL_WARNING("array str error e n d: %s.", str); 251 return NULL; 252 } 253 return str + 1; // 跳過']' 254 } 255 256 // 分析對象的子函數 257 static const char* __parse_object(cjson_t item, const char* str) 258 { 259 cjson_t child; 260 if (*str != '{') { 261 SL_WARNING("object str error start: %s.", str); 262 return NULL; 263 } 264 265 item->type = _CJSON_OBJECT; 266 str = __skip(str + 1); 267 if (*str == '}') 268 return str + 1; 269 270 //處理結點, 開始讀取一個 key 271 item->child = child = __cjson_new(); 272 str = __skip(__parse_string(child, str)); 273 if (!str || *str != ':') { 274 SL_WARNING("__skip __parse_string is error : %s!", str); 275 return NULL; 276 } 277 child->key = child->vs; 278 child->vs = NULL; 279 280 str = __skip(__parse_value(child, __skip(str + 1))); 281 if (!str) { 282 SL_WARNING("__skip __parse_string is error 2!"); 283 return NULL; 284 } 285 286 // 遞歸解析 287 while (*str == ',') { 288 cjson_t nitem = __cjson_new(); 289 child->next = nitem; 290 nitem->prev = child; 291 child = nitem; 292 str = __skip(__parse_string(child, __skip(str + 1))); 293 if (!str || *str != ':'){ 294 SL_WARNING("__parse_string need name or no equal ':' %s.", str); 295 return NULL; 296 } 297 child->key = child->vs; 298 child->vs = NULL; 299 300 str = __skip(__parse_value(child, __skip(str+1))); 301 if (!str) { 302 SL_WARNING("__parse_string need item two ':' %s.", str); 303 return NULL; 304 } 305 } 306 307 if (*str != '}') { 308 SL_WARNING("object str error e n d: %s.", str); 309 return NULL; 310 } 311 return str + 1; 312 } 313 314 // 將value 轉換塞入 item json值中一部分 315 static const char* __parse_value(cjson_t item, const char* value) 316 { 317 char c; 318 if ((value) && (c = *value)) { 319 switch (c) { 320 // n = null, f = false, t = true 321 case 'n' : return item->type = _CJSON_NULL, value + 4; 322 case 'f' : return item->type = _CJSON_FALSE, value + 5; 323 case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4; 324 case '\"': return __parse_string(item, value); 325 case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 326 case '+' : case '-': return __parse_number(item, value); 327 case '[' : return __parse_array(item, value); 328 case '{' : return __parse_object(item, value); 329 } 330 } 331 // 循環到這裡是意外 數據 332 SL_WARNING("params value = %s!", value); 333 return NULL; 334 } 335 336 /* 337 * 對json字符串解析返回解析後的結果 338 * jstr : 待解析的字符串 339 * : 返回解析好的字符串內容 340 */ 341 cjson_t 342 cjson_parse(const char* jstr) 343 { 344 cjson_t c = __cjson_new(); 345 const char* end; 346 347 if (!(end = __parse_value(c, __skip(jstr)))) { 348 SL_WARNING("__parse_value params end = %s!", end); 349 cjson_delete(&c); 350 return NULL; 351 } 352 353 //這裡是否檢測 返回測試數據 354 return c; 355 } 356 357 /* 358 * 根據 item當前結點的 next 一直尋找到 NULL, 返回個數 359 *推薦是數組使用 360 * array : 待處理的cjson_t數組對象 361 * : 返回這個數組中長度 362 */ 363 int 364 cjson_getlen(cjson_t array) 365 { 366 int len = 0; 367 if (array) 368 for (array = array->child; array; array = array->next) 369 ++len; 370 371 return len; 372 } 373 374 /* 375 * 根據索引得到這個數組中對象 376 * array : 數組對象 377 * idx : 查找的索引 必須 [0,cjson_getlen(array)) 范圍內 378 * : 返回查找到的當前對象 379 */ 380 cjson_t 381 cjson_getarray(cjson_t array, int idx) 382 { 383 cjson_t c; 384 DEBUG_CODE({ 385 if (!array || idx < 0) { 386 SL_FATAL("array:%p, idx=%d params is error!", array, idx); 387 return NULL; 388 } 389 }); 390 391 for (c = array->child; c&&idx > 0; c = c->next) 392 --idx; 393 394 return c; 395 } 396 397 /* 398 * 根據key得到這個對象 相應位置的值 399 * object : 待處理對象中值 400 * key : 尋找的key 401 * : 返回 查找 cjson_t 對象 402 */ 403 cjson_t 404 cjson_getobject(cjson_t object, const char* key) 405 { 406 cjson_t c; 407 DEBUG_CODE({ 408 if (!object || !key || !*key) { 409 SL_FATAL("object:%p, key=%s params is error!", object, key); 410 return NULL; 411 } 412 }); 413 414 for (c = object->child; c && str_icmp(key, c->key); c = c->next) 415 ; 416 417 return c; 418 } 419 420 // --------------------------------- 下面是 cjson 輸出部分的處理代碼 ----------------------------------------- 421 422 // 2^n>=x , n是最小的整數 423 static int __pow2gt(int x) 424 { 425 --x; 426 x |= x >> 1; 427 x |= x >> 2; 428 x |= x >> 4; 429 x |= x >> 8; 430 x |= x >> 16; 431 return x + 1; 432 } 433 434 /* 435 * 這裡使用 tstring 結構 size 這裡表示 字符串總大小,沒有變化 436 * len 表示當前字符串的字符串起始偏移量 即 tstring->str + tstring->len 起始的 437 */ 438 static char* __ensure(tstring p, int need) 439 { 440 char* nbuf; 441 int nsize; 442 if (!p || !p->str) { 443 SL_FATAL("p:%p need:%p is error!", p, need); 444 return NULL; 445 } 446 need += p->len; 447 if (need <= p->size) //內存夠用直接返回結果 448 return p->str + p->len; 449 nsize = __pow2gt(need); 450 if ((nbuf = malloc(nsize*sizeof(char))) == NULL) { 451 free(p->str); 452 p->size = p->len = 0; 453 p->str = NULL; 454 SL_FATAL("malloc nsize = %d error!", nsize); 455 return NULL; 456 } 457 //這裡復制內容 458 memcpy(nbuf, p->str, p->size); 459 free(p->str); 460 p->size = nsize; 461 p->str = nbuf; 462 return nbuf + p->len; 463 } 464 465 // 這裡更新一下 當前字符串, 返回當前字符串的長度 466 inline static int __update(tstring p) 467 { 468 return (!p || !p->str) ? 0 : p->len + strlen(p->str+p->len); 469 } 470 471 // 將item 中值轉換成字符串 保存到p中 472 static char* __print_number(cjson_t item, tstring p) 473 { 474 char* str = NULL; 475 double d = item->vd; 476 int i = (int)d; 477 478 if (d == 0) { //普通0 479 str = __ensure(p, 2); 480 if (str) 481 str[0] = '0', str[1] = '\0'; 482 } 483 else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) { 484 str = __ensure(p, 21); //int 值 485 if (str) 486 sprintf(str, "%d", i); 487 } 488 else { 489 str = __ensure(p, 64); //double值 490 if (str) { 491 double nd = fabs(d); //得到正值開始比較 492 if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60) 493 sprintf(str, "%.0f", d); 494 else if(nd < 1.0e-6 || nd > 1.0e9) //科學計數法 495 sprintf(str, "%e", d); 496 else 497 sprintf(str, "%f", d); 498 499 } 500 } 501 502 return str; 503 } 504 505 // 輸出字符串內容 506 static char* __print_string(char* str, tstring p) 507 { 508 const char* ptr; 509 char *nptr, *out; 510 int len = 0, flag = 0; 511 unsigned char c; 512 513 if (!str || !*str) { //最特殊情況,什麼都沒有 返回NULL 514 out = __ensure(p, 3); 515 if (!out) 516 return NULL; 517 out[0] = '\"', out[1] = '\"', out[2] = '\0'; 518 return out; 519 } 520 521 522 for (ptr = str; (c=*ptr); ++ptr) 523 flag |= ((c > 0 && c < 32) || c == '\"' || c == '\\'); 524 525 if (!flag) { //沒有特殊字符直接處理結果 526 len = ptr - str; 527 out = __ensure(p,len + 3); 528 if (!out) 529 return NULL; 530 nptr = out; 531 *nptr++ = '\"'; 532 strcpy(nptr, str); 533 nptr[len] = '\"'; 534 nptr[len + 1] = '\0'; 535 return out; 536 } 537 538 //處理 存在 "和轉義字符內容 539 for (ptr = str; (c = *ptr) && ++len; ++ptr) { 540 if (strchr("\"\\\b\f\n\r\t", c)) 541 ++len; 542 else if (c < 32) //隱藏字符的處理, 這裡可以改 543 len += 5; 544 } 545 546 if ((out = __ensure(p, len + 3)) == NULL) 547 return NULL; 548 //先添加 \" 549 nptr = out; 550 *nptr++ = '\"'; 551 for (ptr = str; (c = *ptr); ++ptr) { 552 if (c > 31 && c != '\"' && c != '\\') { 553 *nptr++ = c; 554 continue; 555 } 556 *nptr++ = '\\'; 557 switch (c){ 558 case '\\': *nptr++ = '\\'; break; 559 case '\"': *nptr++ = '\"'; break; 560 case '\b': *nptr++ = 'b'; break; 561 case '\f': *nptr++ = 'f'; break; 562 case '\n': *nptr++ = 'n'; break; 563 case '\r': *nptr++ = 'r'; break; 564 case '\t': *nptr++ = 't'; break; 565 default: sprintf(nptr, "u%04x", c);nptr += 5; /* 不可見字符 采用 4字節字符編碼 */ 566 } 567 } 568 *nptr++ = '\"'; 569 *nptr = '\0'; 570 return out; 571 } 572 573 //這裡是 遞歸下降 的函數聲明處, 分別是處理值, 數組, object 574 static char* __print_value(cjson_t item, tstring p); 575 static char* __print_array(cjson_t item, tstring p); 576 static char* __print_object(cjson_t item, tstring p); 577 578 // 定義實現部分, 內部私有函數 認為 item 和 p都是存在的 579 static char* __print_value(cjson_t item, tstring p) 580 { 581 char* out = NULL; 582 switch ((item->type) & UCHAR_MAX) { // 0xff 583 case _CJSON_FALSE: if ((out = __ensure(p, 6))) strcpy(out, "false"); break; 584 case _CJSON_TRUE: if ((out = __ensure(p, 5))) strcpy(out, "true"); break; 585 case _CJSON_NULL: if ((out = __ensure(p, 5))) strcpy(out, "null"); break; 586 case _CJSON_NUMBER: out = __print_number(item, p); break; 587 case _CJSON_STRING: out = __print_string(item->vs, p); break; 588 case _CJSON_ARRAY: out = __print_array(item, p); break; 589 case _CJSON_OBJECT: out = __print_object(item, p); break; 590 } 591 592 return out; 593 } 594 595 // 同樣 假定 item 和 p都是存在且不為NULL 596 static char* __print_array(cjson_t item, tstring p) 597 { 598 char* ptr; 599 cjson_t child = item->child; 600 int ncut, i; 601 // 得到孩子結點的深度 602 for (ncut = 0; (child); child = child->child) 603 ++ncut; 604 if (!ncut) { //沒有孩子結點 直接空數組返回結果 605 char* out = NULL; 606 if (!(out = __ensure(p, 3))) 607 strcpy(out, "[]"); 608 return out; 609 } 610 611 i = p->len; 612 if (!(ptr = __ensure(p, 1))) 613 return NULL; 614 *ptr = '['; 615 ++p->len; 616 for (child = item->child; (child); child = child->next) { 617 __print_value(child, p); 618 p->len = __update(p); 619 if (child->next) { 620 if (!(ptr = __ensure(p, 2))) 621 return NULL; 622 *ptr++ = ','; 623 *ptr = '\0'; 624 p->len += 1; 625 } 626 } 627 if (!(ptr = __ensure(p, 2))) 628 return NULL; 629 *ptr++ = ']'; 630 *ptr = '\0'; 631 return p->str + i; 632 633 } 634 635 // 同樣 假定 item 和 p都是存在且不為NULL, 相信這些代碼是安全的 636 static char* __print_object(cjson_t item, tstring p) 637 { 638 char* ptr; 639 int i, ncut, len; 640 cjson_t child = item->child; 641 642 // 得到孩子結點的深度 643 for (ncut = 0; child; child = child->child) 644 ++ncut; 645 if (!ncut) { 646 char* out = NULL; 647 if (!(out = __ensure(p, 3))) 648 strcpy(out, "{}"); 649 return out; 650 } 651 652 i = p->len; 653 if (!(ptr = __ensure(p, 2))) 654 return NULL; 655 *ptr++ = '{'; 656 *ptr -= '\0'; 657 p->len += 1; 658 // 根據子結點 處理 659 for (child = item->child; (child); child = child->next) { 660 __print_string(child->key, p); 661 p->len = __update(p); 662 663 //加入一個冒號 664 if (!(ptr = __ensure(p, 1))) 665 return NULL; 666 *ptr++ = ':'; 667 p->len += 1; 668 669 //繼續打印一個值 670 __print_value(child, p); 671 p->len = __update(p); 672 673 //結算最後內容 674 len = child->next ? 1 : 0; 675 if ((ptr = __ensure(p, len + 1)) == NULL) 676 return NULL; 677 if (child->next) 678 *ptr++ = ','; 679 *ptr = '\0'; 680 p->len += len; 681 } 682 if (!(ptr = __ensure(p, 2))) 683 return NULL; 684 *ptr++ = '}'; 685 *ptr = '\0'; 686 return p->str + i; 687 } 688 689 #define _INT_CJONSTR (256) 690 /* 691 * 這裡是將 cjson_t item 轉換成字符串內容,需要自己free 692 * item : cjson的具體結點 693 * : 返回生成的item的json串內容 694 */ 695 char* 696 cjson_print(cjson_t item) 697 { 698 struct tstring p; 699 char* out; 700 if ((!item) || !(p.str = malloc(sizeof(char)*_INT_CJONSTR))) { 701 SL_FATAL("item:%p, p.str = malloc is error!", item); 702 return NULL; 703 } 704 p.size = _INT_CJONSTR; 705 p.len = 0; 706 707 out = __print_value(item, &p); //從值處理開始, 返回最終結果 708 if (out == NULL) { 709 free(p.str); 710 SL_FATAL("__print_value item:%p, p:%p is error!", item, &p); 711 return NULL; 712 } 713 return realloc(out,strlen(out) + 1); // 體積變小 realloc返回一定成功 714 } 715 716 // --------------------------------- 下面是 cjson 輸出部分的輔助代碼 ----------------------------------------- View Code

到這裡基本前期主場都完了. 後面就是玩測試了.

 

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":1,
    "dungeon_id":40008,
    "level_contain":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
    "active_time":[[1,"110000"],[4,"110000"],[5,"210000"]],
    "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":[[8,1000],[9,1000],[11,1000],[12,1000]],
    "jinbi_buff_price":10,
    "jinbi_buff_limit":999,
    "free_change":1,
    "refresh_price":20,
    "change_price":20,
    "show_hero_num":20 
   }  
 }    
}     

解析成我們想要的那樣的東西. 嘗試, 追求, 按部就班 , 人生...........................................

 

後記 

  錯誤是難免,歡迎批評指正. 下次會對cjson 進行簡單總結, 再添加一些輔助函數. 一切會從簡單來.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved